def _serialize_gate_op(
        self,
        op: cirq.Operation,
        msg: v2.program_pb2.Operation,
        *,
        constants: list[v2.program_pb2.Constant],
        raw_constants: dict[Any, int],
    ) -> v2.program_pb2.Operation:
        """Serialize an Operation to cirq_google.api.v2.Operation proto.

        Args:
            op: The operation to serialize.
            msg: An optional proto object to populate with the serialization
                results.
            constants: The list of previously-serialized Constant protos.
            raw_constants: A map raw objects to their respective indices in
                `constants`.

        Returns:
            The cirq.google.api.v2.Operation proto.

        Raises:
            ValueError: If the operation cannot be serialized.
        """
        gate = op.gate
        if isinstance(op, cirq.ClassicallyControlledOperation):
            gate = op.without_classical_controls().gate
            for control in op.classical_controls:
                arg_func_langs.condition_to_proto(control, out=msg.conditioned_on.add())
        if isinstance(gate, InternalGate):
            arg_func_langs.internal_gate_arg_to_proto(gate, out=msg.internalgate)
        elif isinstance(gate, cirq.XPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.xpowgate.exponent)
        elif isinstance(gate, cirq.YPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.ypowgate.exponent)
        elif isinstance(gate, cirq.ZPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.zpowgate.exponent)
            if any(isinstance(tag, PhysicalZTag) for tag in op.tags):
                msg.zpowgate.is_physical_z = True
        elif isinstance(gate, cirq.PhasedXPowGate):
            arg_func_langs.float_arg_to_proto(
                gate.phase_exponent, out=msg.phasedxpowgate.phase_exponent
            )
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.phasedxpowgate.exponent)
        elif isinstance(gate, cirq.PhasedXZGate):
            arg_func_langs.float_arg_to_proto(gate.x_exponent, out=msg.phasedxzgate.x_exponent)
            arg_func_langs.float_arg_to_proto(gate.z_exponent, out=msg.phasedxzgate.z_exponent)
            arg_func_langs.float_arg_to_proto(
                gate.axis_phase_exponent, out=msg.phasedxzgate.axis_phase_exponent
            )
        elif isinstance(gate, cirq.ops.SingleQubitCliffordGate):
            arg_func_langs.clifford_tableau_arg_to_proto(
                gate._clifford_tableau, out=msg.singlequbitcliffordgate.tableau
            )
        elif isinstance(gate, cirq.ops.IdentityGate):
            msg.identitygate.qid_shape.extend(cirq.qid_shape(gate))
        elif isinstance(gate, cirq.HPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.hpowgate.exponent)
        elif isinstance(gate, cirq.CZPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.czpowgate.exponent)
        elif isinstance(gate, cirq.ISwapPowGate):
            arg_func_langs.float_arg_to_proto(gate.exponent, out=msg.iswappowgate.exponent)
        elif isinstance(gate, SycamoreGate):
            msg.iswaplikegate.original_gate = v2.program_pb2.ISwapLikeGate.OriginalCirqGate.SYCAMORE
        elif isinstance(gate, WillowGate):
            msg.iswaplikegate.original_gate = v2.program_pb2.ISwapLikeGate.OriginalCirqGate.WILLOW
        elif isinstance(gate, cirq.FSimGate):
            arg_func_langs.float_arg_to_proto(gate.theta, out=msg.fsimgate.theta)
            arg_func_langs.float_arg_to_proto(gate.phi, out=msg.fsimgate.phi)
            if any(isinstance(tag, FSimViaModelTag) for tag in op.tags):
                msg.fsimgate.translate_via_model = True
        elif isinstance(gate, cirq.MeasurementGate):
            arg_func_langs.arg_to_proto(gate.key, out=msg.measurementgate.key)
            if len(gate.invert_mask):
                # Do not serialize empty invert mask until servers support empty tuples
                arg_func_langs.arg_to_proto(gate.invert_mask, out=msg.measurementgate.invert_mask)
        elif isinstance(gate, cirq.WaitGate):
            arg_func_langs.float_arg_to_proto(
                gate.duration.total_nanos(), out=msg.waitgate.duration_nanos
            )
        elif isinstance(gate, cirq.ResetChannel):
            arg_func_langs.arg_to_proto(gate.dimension, out=msg.resetgate.arguments['dimension'])
        elif isinstance(gate, CouplerPulse):
            arg_func_langs.float_arg_to_proto(
                gate.hold_time.total_picos(), out=msg.couplerpulsegate.hold_time_ps
            )
            arg_func_langs.float_arg_to_proto(
                gate.rise_time.total_picos(), out=msg.couplerpulsegate.rise_time_ps
            )
            arg_func_langs.float_arg_to_proto(
                gate.padding_time.total_picos(), out=msg.couplerpulsegate.padding_time_ps
            )
            arg_func_langs.float_arg_to_proto(
                gate.coupling_mhz, out=msg.couplerpulsegate.coupling_mhz
            )
            arg_func_langs.float_arg_to_proto(
                gate.q0_detune_mhz, out=msg.couplerpulsegate.q0_detune_mhz
            )
            arg_func_langs.float_arg_to_proto(
                gate.q1_detune_mhz, out=msg.couplerpulsegate.q1_detune_mhz
            )
        else:
            raise ValueError(f'Cannot serialize op {op!r} of type {type(gate)}')

        for qubit in op.qubits:
            if qubit not in raw_constants:
                constants.append(
                    v2.program_pb2.Constant(
                        qubit=v2.program_pb2.Qubit(id=v2.qubit_to_proto_id(qubit))
                    )
                )
                raw_constants[qubit] = len(constants) - 1
            msg.qubit_constant_index.append(raw_constants[qubit])

        for tag in op.tags:
            constant = v2.program_pb2.Constant()
            if isinstance(tag, CalibrationTag):
                constant.string_value = tag.token
                if tag.token in raw_constants:
                    msg.token_constant_index = raw_constants[tag.token]
                else:
                    # Token not found, add it to the list
                    msg.token_constant_index = len(constants)
                    constants.append(constant)
                    if raw_constants is not None:
                        raw_constants[tag.token] = msg.token_constant_index
            else:
                if isinstance(tag, DynamicalDecouplingTag):
                    # TODO(dstrain): Remove this once we are deserializing tag indices everywhere.
                    tag.to_proto(msg=msg.tags.add())
                if (tag_index := raw_constants.get(tag, None)) is None:
                    if self.tag_serializer and self.tag_serializer.can_serialize_tag(tag):
                        self.tag_serializer.to_proto(
                            tag,
                            msg=constant.tag_value,
                            constants=constants,
                            raw_constants=raw_constants,
                        )
                    elif getattr(tag, 'to_proto', None) is not None:
                        tag.to_proto(constant.tag_value)  # type: ignore
                    else:
                        warnings.warn(f'Unrecognized Tag {tag}, not serializing.')
                    if constant.WhichOneof('const_value'):
                        constants.append(constant)
                        if raw_constants is not None:
                            raw_constants[tag] = len(constants) - 1
                        msg.tag_indices.append(len(constants) - 1)
                else:
                    msg.tag_indices.append(tag_index)
        return msg
Serialize an Operation to cirq_google.api.v2.Operation proto.

        Args:
            op: The operation to serialize.
            msg: An optional proto object to populate with the serialization
                results.
            constants: The list of previously-serialized Constant protos.
            raw_constants: A map raw objects to their respective indices in
                `constants`.

        Returns:
            The cirq.google.api.v2.Operation proto.

        Raises:
            ValueError: If the operation cannot be serialized.
        
cirq-google/cirq_google/serialization/circuit_serializer.py