Repository: quantumlib/Cirq

Issue:
Cannot serialize identity gate on Quantum Engine
**Description of the issue**

Cannot serialize identity gate when using SQRT_ISWAP_GATESET.

**How to reproduce the issue**

```python
sampler = engine.get_sampler(processor_id='...', gate_set=cg.SQRT_ISWAP_GATESET)
q = cirq.GridQubit(7, 4)
c = cirq.Circuit(cirq.I(q))
r = sampler.run(c, repetitions=1)
...
ValueError: Cannot serialize op cirq.I(cirq.GridQubit(7, 4)) of type <class 'cirq.ops.identity.IdentityGate'>
```

**Cirq version**

```
0.14.0.dev20211210220312
```




Discussion:
If we decide to add serialization for Identity, it should also be added to the new `CircuitSerializer`. 

https://github.com/quantumlib/Cirq/blob/89f88b01d69222d3f1ec14d649b7b3a85ed9211f/cirq-google/cirq_google/serialization/circuit_serializer.py#L120
I might be interested, but I can certainly yield on this one.
I don't think this gate should be serialized, as it has no effect by design in the hardware (and this serialization is only for Google hardware).

I would support cirq.I gates being stripped by default from serialization.
Confirmed with @viathor that we should just strip these gates out when serializing.  Let's do this in CircuitSerializer too.  @verult 
Per @maffoo 's [comment](https://github.com/quantumlib/Cirq/pull/4994#pullrequestreview-882489134), there is a use case for serialize identity gates, and they should be serialized along with their tags. And in general, a user's circuit should be serialized as-is as much as possible to avoid surprises.
@maffoo is identity gate tag serialization a use case we need to support right away? If not, I propose piggybacking on an existing gate type in the `Program` proto until the use case comes along.
Chatted with @verult about this; IMO we should add a separate `IdentityGate` case in the [`Operation` proto](https://github.com/quantumlib/Cirq/blob/master/cirq-google/cirq_google/api/v2/program.proto#L195) if we want to serialize identities, because the serialization tries to preserve gates as closely as possible instead of converting to "equivalent" gates (e.g. we have separate cases for `XPowGate` vs `PhaseXZGate`). Until then, I think we should fail if trying to serialize identity gates, and it would be up to the user to remove them (we can add helper function to do this if needed).
>  (we can add helper function to do this if needed).

Just FYI: We already have `cirq.drop_negligible_operations` transformer to achieve this. 

https://github.com/quantumlib/Cirq/blob/afb62da0e33559a47135094047ce3a872740a17e/cirq-core/cirq/transformers/drop_negligible_operations.py#L26
What about GlobalPhaseGates? I'm assuming they are ignored by hardware as well, so they should be handled the same way?
Yes, I would suggest handling them in the same way. When serializing, fail if we encounter unsupported gates. We can add transformer support for removing them from circuits, or we can add cases to the Operation proto to allow serializing them if desired.
We could probably serialize both for ease of user use, but, since this is not backwards incompatible, and there is a easy work-around, I think this can be post-1.0 if necessary.
@NoureldinYosri This is a low priority item, but if you have time, should be very similar to some similar bugs you handled recently.

Resolving patch:
diff --git a/cirq-google/cirq_google/api/v2/program_pb2.py b/cirq-google/cirq_google/api/v2/program_pb2.py
--- a/cirq-google/cirq_google/api/v2/program_pb2.py
+++ b/cirq-google/cirq_google/api/v2/program_pb2.py
@@ -13,7 +13,7 @@
 
 
 
-DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n cirq_google/api/v2/program.proto\x12\x12\x63irq.google.api.v2\"\xd7\x01\n\x07Program\x12.\n\x08language\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.Language\x12.\n\x07\x63ircuit\x18\x02 \x01(\x0b\x32\x1b.cirq.google.api.v2.CircuitH\x00\x12\x30\n\x08schedule\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.ScheduleH\x00\x12/\n\tconstants\x18\x04 \x03(\x0b\x32\x1c.cirq.google.api.v2.ConstantB\t\n\x07program\"\x93\x01\n\x08\x43onstant\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x34\n\rcircuit_value\x18\x02 \x01(\x0b\x32\x1b.cirq.google.api.v2.CircuitH\x00\x12*\n\x05qubit\x18\x03 \x01(\x0b\x32\x19.cirq.google.api.v2.QubitH\x00\x42\r\n\x0b\x63onst_value\"\xd4\x01\n\x07\x43ircuit\x12K\n\x13scheduling_strategy\x18\x01 \x01(\x0e\x32..cirq.google.api.v2.Circuit.SchedulingStrategy\x12+\n\x07moments\x18\x02 \x03(\x0b\x32\x1a.cirq.google.api.v2.Moment\"O\n\x12SchedulingStrategy\x12#\n\x1fSCHEDULING_STRATEGY_UNSPECIFIED\x10\x00\x12\x14\n\x10MOMENT_BY_MOMENT\x10\x01\"}\n\x06Moment\x12\x31\n\noperations\x18\x01 \x03(\x0b\x32\x1d.cirq.google.api.v2.Operation\x12@\n\x12\x63ircuit_operations\x18\x02 \x03(\x0b\x32$.cirq.google.api.v2.CircuitOperation\"P\n\x08Schedule\x12\x44\n\x14scheduled_operations\x18\x03 \x03(\x0b\x32&.cirq.google.api.v2.ScheduledOperation\"`\n\x12ScheduledOperation\x12\x30\n\toperation\x18\x01 \x01(\x0b\x32\x1d.cirq.google.api.v2.Operation\x12\x18\n\x10start_time_picos\x18\x02 \x01(\x03\"?\n\x08Language\x12\x14\n\x08gate_set\x18\x01 \x01(\tB\x02\x18\x01\x12\x1d\n\x15\x61rg_function_language\x18\x02 \x01(\t\"k\n\x08\x46loatArg\x12\x15\n\x0b\x66loat_value\x18\x01 \x01(\x02H\x00\x12\x10\n\x06symbol\x18\x02 \x01(\tH\x00\x12/\n\x04\x66unc\x18\x03 \x01(\x0b\x32\x1f.cirq.google.api.v2.ArgFunctionH\x00\x42\x05\n\x03\x61rg\":\n\x08XPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\":\n\x08YPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"Q\n\x08ZPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x15\n\ris_physical_z\x18\x02 \x01(\x08\"v\n\x0ePhasedXPowGate\x12\x34\n\x0ephase_exponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12.\n\x08\x65xponent\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"\xad\x01\n\x0cPhasedXZGate\x12\x30\n\nx_exponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x30\n\nz_exponent\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x39\n\x13\x61xis_phase_exponent\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\";\n\tCZPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"b\n\x08\x46SimGate\x12+\n\x05theta\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12)\n\x03phi\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\">\n\x0cISwapPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"e\n\x0fMeasurementGate\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\x12,\n\x0binvert_mask\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\"@\n\x08WaitGate\x12\x34\n\x0e\x64uration_nanos\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"\xeb\x07\n\tOperation\x12*\n\x04gate\x18\x01 \x01(\x0b\x32\x18.cirq.google.api.v2.GateB\x02\x18\x01\x12\x30\n\x08xpowgate\x18\x07 \x01(\x0b\x32\x1c.cirq.google.api.v2.XPowGateH\x00\x12\x30\n\x08ypowgate\x18\x08 \x01(\x0b\x32\x1c.cirq.google.api.v2.YPowGateH\x00\x12\x30\n\x08zpowgate\x18\t \x01(\x0b\x32\x1c.cirq.google.api.v2.ZPowGateH\x00\x12<\n\x0ephasedxpowgate\x18\n \x01(\x0b\x32\".cirq.google.api.v2.PhasedXPowGateH\x00\x12\x38\n\x0cphasedxzgate\x18\x0b \x01(\x0b\x32 .cirq.google.api.v2.PhasedXZGateH\x00\x12\x32\n\tczpowgate\x18\x0c \x01(\x0b\x32\x1d.cirq.google.api.v2.CZPowGateH\x00\x12\x30\n\x08\x66simgate\x18\r \x01(\x0b\x32\x1c.cirq.google.api.v2.FSimGateH\x00\x12\x38\n\x0ciswappowgate\x18\x0e \x01(\x0b\x32 .cirq.google.api.v2.ISwapPowGateH\x00\x12>\n\x0fmeasurementgate\x18\x0f \x01(\x0b\x32#.cirq.google.api.v2.MeasurementGateH\x00\x12\x30\n\x08waitgate\x18\x10 \x01(\x0b\x32\x1c.cirq.google.api.v2.WaitGateH\x00\x12\x38\n\x0cinternalgate\x18\x11 \x01(\x0b\x32 .cirq.google.api.v2.InternalGateH\x00\x12@\n\x10\x63ouplerpulsegate\x18\x12 \x01(\x0b\x32$.cirq.google.api.v2.CouplerPulseGateH\x00\x12\x39\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\'.cirq.google.api.v2.Operation.ArgsEntryB\x02\x18\x01\x12)\n\x06qubits\x18\x03 \x03(\x0b\x32\x19.cirq.google.api.v2.Qubit\x12\x1c\n\x14qubit_constant_index\x18\x06 \x03(\x05\x12\x15\n\x0btoken_value\x18\x04 \x01(\tH\x01\x12\x1e\n\x14token_constant_index\x18\x05 \x01(\x05H\x01\x1a\x44\n\tArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg:\x02\x38\x01\x42\x0c\n\ngate_valueB\x07\n\x05token\"\x12\n\x04Gate\x12\n\n\x02id\x18\x01 \x01(\t\"\x13\n\x05Qubit\x12\n\n\x02id\x18\x02 \x01(\t\"\x9c\x01\n\x03\x41rg\x12\x31\n\targ_value\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.ArgValueH\x00\x12\x10\n\x06symbol\x18\x02 \x01(\tH\x00\x12/\n\x04\x66unc\x18\x03 \x01(\x0b\x32\x1f.cirq.google.api.v2.ArgFunctionH\x00\x12\x18\n\x0e\x63onstant_index\x18\x04 \x01(\x05H\x00\x42\x05\n\x03\x61rg\"\xcf\x02\n\x08\x41rgValue\x12\x15\n\x0b\x66loat_value\x18\x01 \x01(\x02H\x00\x12:\n\x0b\x62ool_values\x18\x02 \x01(\x0b\x32#.cirq.google.api.v2.RepeatedBooleanH\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12\x39\n\x0cint64_values\x18\x05 \x01(\x0b\x32!.cirq.google.api.v2.RepeatedInt64H\x00\x12;\n\rdouble_values\x18\x06 \x01(\x0b\x32\".cirq.google.api.v2.RepeatedDoubleH\x00\x12;\n\rstring_values\x18\x07 \x01(\x0b\x32\".cirq.google.api.v2.RepeatedStringH\x00\x42\x0b\n\targ_value\"\x1f\n\rRepeatedInt64\x12\x0e\n\x06values\x18\x01 \x03(\x03\" \n\x0eRepeatedDouble\x12\x0e\n\x06values\x18\x01 \x03(\x01\" \n\x0eRepeatedString\x12\x0e\n\x06values\x18\x01 \x03(\t\"!\n\x0fRepeatedBoolean\x12\x0e\n\x06values\x18\x01 \x03(\x08\"B\n\x0b\x41rgFunction\x12\x0c\n\x04type\x18\x01 \x01(\t\x12%\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\x17.cirq.google.api.v2.Arg\"\xaf\x02\n\x10\x43ircuitOperation\x12\x1e\n\x16\x63ircuit_constant_index\x18\x01 \x01(\x05\x12M\n\x18repetition_specification\x18\x02 \x01(\x0b\x32+.cirq.google.api.v2.RepetitionSpecification\x12\x33\n\tqubit_map\x18\x03 \x01(\x0b\x32 .cirq.google.api.v2.QubitMapping\x12\x46\n\x13measurement_key_map\x18\x04 \x01(\x0b\x32).cirq.google.api.v2.MeasurementKeyMapping\x12/\n\x07\x61rg_map\x18\x05 \x01(\x0b\x32\x1e.cirq.google.api.v2.ArgMapping\"\xbc\x01\n\x17RepetitionSpecification\x12S\n\x0erepetition_ids\x18\x01 \x01(\x0b\x32\x39.cirq.google.api.v2.RepetitionSpecification.RepetitionIdsH\x00\x12\x1a\n\x10repetition_count\x18\x02 \x01(\x05H\x00\x1a\x1c\n\rRepetitionIds\x12\x0b\n\x03ids\x18\x01 \x03(\tB\x12\n\x10repetition_value\"\xac\x01\n\x0cQubitMapping\x12<\n\x07\x65ntries\x18\x01 \x03(\x0b\x32+.cirq.google.api.v2.QubitMapping.QubitEntry\x1a^\n\nQubitEntry\x12&\n\x03key\x18\x01 \x01(\x0b\x32\x19.cirq.google.api.v2.Qubit\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.cirq.google.api.v2.Qubit\"$\n\x0eMeasurementKey\x12\x12\n\nstring_key\x18\x01 \x01(\t\"\xe2\x01\n\x15MeasurementKeyMapping\x12N\n\x07\x65ntries\x18\x01 \x03(\x0b\x32=.cirq.google.api.v2.MeasurementKeyMapping.MeasurementKeyEntry\x1ay\n\x13MeasurementKeyEntry\x12/\n\x03key\x18\x01 \x01(\x0b\x32\".cirq.google.api.v2.MeasurementKey\x12\x31\n\x05value\x18\x02 \x01(\x0b\x32\".cirq.google.api.v2.MeasurementKey\"\xa0\x01\n\nArgMapping\x12\x38\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\'.cirq.google.api.v2.ArgMapping.ArgEntry\x1aX\n\x08\x41rgEntry\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\"\xcd\x01\n\x0cInternalGate\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06module\x18\x02 \x01(\t\x12\x12\n\nnum_qubits\x18\x03 \x01(\x05\x12\x41\n\tgate_args\x18\x04 \x03(\x0b\x32..cirq.google.api.v2.InternalGate.GateArgsEntry\x1aH\n\rGateArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg:\x02\x38\x01\"\xd8\x03\n\x10\x43ouplerPulseGate\x12\x37\n\x0chold_time_ps\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x00\x88\x01\x01\x12\x37\n\x0crise_time_ps\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x01\x88\x01\x01\x12:\n\x0fpadding_time_ps\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x02\x88\x01\x01\x12\x37\n\x0c\x63oupling_mhz\x18\x04 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x03\x88\x01\x01\x12\x38\n\rq0_detune_mhz\x18\x05 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x04\x88\x01\x01\x12\x38\n\rq1_detune_mhz\x18\x06 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x05\x88\x01\x01\x42\x0f\n\r_hold_time_psB\x0f\n\r_rise_time_psB\x12\n\x10_padding_time_psB\x0f\n\r_coupling_mhzB\x10\n\x0e_q0_detune_mhzB\x10\n\x0e_q1_detune_mhzB/\n\x1d\x63om.google.cirq.google.api.v2B\x0cProgramProtoP\x01\x62\x06proto3')
+DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n cirq_google/api/v2/program.proto\x12\x12\x63irq.google.api.v2\"\xd7\x01\n\x07Program\x12.\n\x08language\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.Language\x12.\n\x07\x63ircuit\x18\x02 \x01(\x0b\x32\x1b.cirq.google.api.v2.CircuitH\x00\x12\x30\n\x08schedule\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.ScheduleH\x00\x12/\n\tconstants\x18\x04 \x03(\x0b\x32\x1c.cirq.google.api.v2.ConstantB\t\n\x07program\"\x93\x01\n\x08\x43onstant\x12\x16\n\x0cstring_value\x18\x01 \x01(\tH\x00\x12\x34\n\rcircuit_value\x18\x02 \x01(\x0b\x32\x1b.cirq.google.api.v2.CircuitH\x00\x12*\n\x05qubit\x18\x03 \x01(\x0b\x32\x19.cirq.google.api.v2.QubitH\x00\x42\r\n\x0b\x63onst_value\"\xd4\x01\n\x07\x43ircuit\x12K\n\x13scheduling_strategy\x18\x01 \x01(\x0e\x32..cirq.google.api.v2.Circuit.SchedulingStrategy\x12+\n\x07moments\x18\x02 \x03(\x0b\x32\x1a.cirq.google.api.v2.Moment\"O\n\x12SchedulingStrategy\x12#\n\x1fSCHEDULING_STRATEGY_UNSPECIFIED\x10\x00\x12\x14\n\x10MOMENT_BY_MOMENT\x10\x01\"}\n\x06Moment\x12\x31\n\noperations\x18\x01 \x03(\x0b\x32\x1d.cirq.google.api.v2.Operation\x12@\n\x12\x63ircuit_operations\x18\x02 \x03(\x0b\x32$.cirq.google.api.v2.CircuitOperation\"P\n\x08Schedule\x12\x44\n\x14scheduled_operations\x18\x03 \x03(\x0b\x32&.cirq.google.api.v2.ScheduledOperation\"`\n\x12ScheduledOperation\x12\x30\n\toperation\x18\x01 \x01(\x0b\x32\x1d.cirq.google.api.v2.Operation\x12\x18\n\x10start_time_picos\x18\x02 \x01(\x03\"?\n\x08Language\x12\x14\n\x08gate_set\x18\x01 \x01(\tB\x02\x18\x01\x12\x1d\n\x15\x61rg_function_language\x18\x02 \x01(\t\"k\n\x08\x46loatArg\x12\x15\n\x0b\x66loat_value\x18\x01 \x01(\x02H\x00\x12\x10\n\x06symbol\x18\x02 \x01(\tH\x00\x12/\n\x04\x66unc\x18\x03 \x01(\x0b\x32\x1f.cirq.google.api.v2.ArgFunctionH\x00\x42\x05\n\x03\x61rg\":\n\x08XPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\":\n\x08YPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"Q\n\x08ZPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x15\n\ris_physical_z\x18\x02 \x01(\x08\"v\n\x0ePhasedXPowGate\x12\x34\n\x0ephase_exponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12.\n\x08\x65xponent\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"\xad\x01\n\x0cPhasedXZGate\x12\x30\n\nx_exponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x30\n\nz_exponent\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12\x39\n\x13\x61xis_phase_exponent\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\";\n\tCZPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"b\n\x08\x46SimGate\x12+\n\x05theta\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\x12)\n\x03phi\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\">\n\x0cISwapPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"e\n\x0fMeasurementGate\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\x12,\n\x0binvert_mask\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\"@\n\x08WaitGate\x12\x34\n\x0e\x64uration_nanos\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArg\"\xa7\t\n\tOperation\x12*\n\x04gate\x18\x01 \x01(\x0b\x32\x18.cirq.google.api.v2.GateB\x02\x18\x01\x12\x30\n\x08xpowgate\x18\x07 \x01(\x0b\x32\x1c.cirq.google.api.v2.XPowGateH\x00\x12\x30\n\x08ypowgate\x18\x08 \x01(\x0b\x32\x1c.cirq.google.api.v2.YPowGateH\x00\x12\x30\n\x08zpowgate\x18\t \x01(\x0b\x32\x1c.cirq.google.api.v2.ZPowGateH\x00\x12<\n\x0ephasedxpowgate\x18\n \x01(\x0b\x32\".cirq.google.api.v2.PhasedXPowGateH\x00\x12\x38\n\x0cphasedxzgate\x18\x0b \x01(\x0b\x32 .cirq.google.api.v2.PhasedXZGateH\x00\x12\x32\n\tczpowgate\x18\x0c \x01(\x0b\x32\x1d.cirq.google.api.v2.CZPowGateH\x00\x12\x30\n\x08\x66simgate\x18\r \x01(\x0b\x32\x1c.cirq.google.api.v2.FSimGateH\x00\x12\x38\n\x0ciswappowgate\x18\x0e \x01(\x0b\x32 .cirq.google.api.v2.ISwapPowGateH\x00\x12>\n\x0fmeasurementgate\x18\x0f \x01(\x0b\x32#.cirq.google.api.v2.MeasurementGateH\x00\x12\x30\n\x08waitgate\x18\x10 \x01(\x0b\x32\x1c.cirq.google.api.v2.WaitGateH\x00\x12\x38\n\x0cinternalgate\x18\x11 \x01(\x0b\x32 .cirq.google.api.v2.InternalGateH\x00\x12@\n\x10\x63ouplerpulsegate\x18\x12 \x01(\x0b\x32$.cirq.google.api.v2.CouplerPulseGateH\x00\x12\x38\n\x0cidentitygate\x18\x13 \x01(\x0b\x32 .cirq.google.api.v2.IdentityGateH\x00\x12\x30\n\x08hpowgate\x18\x14 \x01(\x0b\x32\x1c.cirq.google.api.v2.HPowGateH\x00\x12N\n\x17singlequbitcliffordgate\x18\x15 \x01(\x0b\x32+.cirq.google.api.v2.SingleQubitCliffordGateH\x00\x12\x39\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\'.cirq.google.api.v2.Operation.ArgsEntryB\x02\x18\x01\x12)\n\x06qubits\x18\x03 \x03(\x0b\x32\x19.cirq.google.api.v2.Qubit\x12\x1c\n\x14qubit_constant_index\x18\x06 \x03(\x05\x12\x15\n\x0btoken_value\x18\x04 \x01(\tH\x01\x12\x1e\n\x14token_constant_index\x18\x05 \x01(\x05H\x01\x1a\x44\n\tArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg:\x02\x38\x01\x42\x0c\n\ngate_valueB\x07\n\x05token\"\x12\n\x04Gate\x12\n\n\x02id\x18\x01 \x01(\t\"\x13\n\x05Qubit\x12\n\n\x02id\x18\x02 \x01(\t\"\x9c\x01\n\x03\x41rg\x12\x31\n\targ_value\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.ArgValueH\x00\x12\x10\n\x06symbol\x18\x02 \x01(\tH\x00\x12/\n\x04\x66unc\x18\x03 \x01(\x0b\x32\x1f.cirq.google.api.v2.ArgFunctionH\x00\x12\x18\n\x0e\x63onstant_index\x18\x04 \x01(\x05H\x00\x42\x05\n\x03\x61rg\"\xcf\x02\n\x08\x41rgValue\x12\x15\n\x0b\x66loat_value\x18\x01 \x01(\x02H\x00\x12:\n\x0b\x62ool_values\x18\x02 \x01(\x0b\x32#.cirq.google.api.v2.RepeatedBooleanH\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x12\x16\n\x0c\x64ouble_value\x18\x04 \x01(\x01H\x00\x12\x39\n\x0cint64_values\x18\x05 \x01(\x0b\x32!.cirq.google.api.v2.RepeatedInt64H\x00\x12;\n\rdouble_values\x18\x06 \x01(\x0b\x32\".cirq.google.api.v2.RepeatedDoubleH\x00\x12;\n\rstring_values\x18\x07 \x01(\x0b\x32\".cirq.google.api.v2.RepeatedStringH\x00\x42\x0b\n\targ_value\"\x1f\n\rRepeatedInt64\x12\x0e\n\x06values\x18\x01 \x03(\x03\" \n\x0eRepeatedDouble\x12\x0e\n\x06values\x18\x01 \x03(\x01\" \n\x0eRepeatedString\x12\x0e\n\x06values\x18\x01 \x03(\t\"!\n\x0fRepeatedBoolean\x12\x0e\n\x06values\x18\x01 \x03(\x08\"B\n\x0b\x41rgFunction\x12\x0c\n\x04type\x18\x01 \x01(\t\x12%\n\x04\x61rgs\x18\x02 \x03(\x0b\x32\x17.cirq.google.api.v2.Arg\"\xaf\x02\n\x10\x43ircuitOperation\x12\x1e\n\x16\x63ircuit_constant_index\x18\x01 \x01(\x05\x12M\n\x18repetition_specification\x18\x02 \x01(\x0b\x32+.cirq.google.api.v2.RepetitionSpecification\x12\x33\n\tqubit_map\x18\x03 \x01(\x0b\x32 .cirq.google.api.v2.QubitMapping\x12\x46\n\x13measurement_key_map\x18\x04 \x01(\x0b\x32).cirq.google.api.v2.MeasurementKeyMapping\x12/\n\x07\x61rg_map\x18\x05 \x01(\x0b\x32\x1e.cirq.google.api.v2.ArgMapping\"\xbc\x01\n\x17RepetitionSpecification\x12S\n\x0erepetition_ids\x18\x01 \x01(\x0b\x32\x39.cirq.google.api.v2.RepetitionSpecification.RepetitionIdsH\x00\x12\x1a\n\x10repetition_count\x18\x02 \x01(\x05H\x00\x1a\x1c\n\rRepetitionIds\x12\x0b\n\x03ids\x18\x01 \x03(\tB\x12\n\x10repetition_value\"\xac\x01\n\x0cQubitMapping\x12<\n\x07\x65ntries\x18\x01 \x03(\x0b\x32+.cirq.google.api.v2.QubitMapping.QubitEntry\x1a^\n\nQubitEntry\x12&\n\x03key\x18\x01 \x01(\x0b\x32\x19.cirq.google.api.v2.Qubit\x12(\n\x05value\x18\x02 \x01(\x0b\x32\x19.cirq.google.api.v2.Qubit\"$\n\x0eMeasurementKey\x12\x12\n\nstring_key\x18\x01 \x01(\t\"\xe2\x01\n\x15MeasurementKeyMapping\x12N\n\x07\x65ntries\x18\x01 \x03(\x0b\x32=.cirq.google.api.v2.MeasurementKeyMapping.MeasurementKeyEntry\x1ay\n\x13MeasurementKeyEntry\x12/\n\x03key\x18\x01 \x01(\x0b\x32\".cirq.google.api.v2.MeasurementKey\x12\x31\n\x05value\x18\x02 \x01(\x0b\x32\".cirq.google.api.v2.MeasurementKey\"\xa0\x01\n\nArgMapping\x12\x38\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\'.cirq.google.api.v2.ArgMapping.ArgEntry\x1aX\n\x08\x41rgEntry\x12$\n\x03key\x18\x01 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg\"\xcd\x01\n\x0cInternalGate\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06module\x18\x02 \x01(\t\x12\x12\n\nnum_qubits\x18\x03 \x01(\x05\x12\x41\n\tgate_args\x18\x04 \x03(\x0b\x32..cirq.google.api.v2.InternalGate.GateArgsEntry\x1aH\n\rGateArgsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12&\n\x05value\x18\x02 \x01(\x0b\x32\x17.cirq.google.api.v2.Arg:\x02\x38\x01\"\xd8\x03\n\x10\x43ouplerPulseGate\x12\x37\n\x0chold_time_ps\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x00\x88\x01\x01\x12\x37\n\x0crise_time_ps\x18\x02 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x01\x88\x01\x01\x12:\n\x0fpadding_time_ps\x18\x03 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x02\x88\x01\x01\x12\x37\n\x0c\x63oupling_mhz\x18\x04 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x03\x88\x01\x01\x12\x38\n\rq0_detune_mhz\x18\x05 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x04\x88\x01\x01\x12\x38\n\rq1_detune_mhz\x18\x06 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgH\x05\x88\x01\x01\x42\x0f\n\r_hold_time_psB\x0f\n\r_rise_time_psB\x12\n\x10_padding_time_psB\x0f\n\r_coupling_mhzB\x10\n\x0e_q0_detune_mhzB\x10\n\x0e_q1_detune_mhz\"\x8b\x01\n\x0f\x43liffordTableau\x12\x17\n\nnum_qubits\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x1a\n\rinitial_state\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12\n\n\x02rs\x18\x03 \x03(\x08\x12\n\n\x02xs\x18\x04 \x03(\x08\x12\n\n\x02zs\x18\x05 \x03(\x08\x42\r\n\x0b_num_qubitsB\x10\n\x0e_initial_state\"O\n\x17SingleQubitCliffordGate\x12\x34\n\x07tableau\x18\x01 \x01(\x0b\x32#.cirq.google.api.v2.CliffordTableau\"!\n\x0cIdentityGate\x12\x11\n\tqid_shape\x18\x01 \x03(\r\":\n\x08HPowGate\x12.\n\x08\x65xponent\x18\x01 \x01(\x0b\x32\x1c.cirq.google.api.v2.FloatArgB/\n\x1d\x63om.google.cirq.google.api.v2B\x0cProgramProtoP\x01\x62\x06proto3')
 
 _globals = globals()
 _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -71,51 +71,59 @@
   _globals['_WAITGATE']._serialized_start=1947
   _globals['_WAITGATE']._serialized_end=2011
   _globals['_OPERATION']._serialized_start=2014
-  _globals['_OPERATION']._serialized_end=3017
-  _globals['_OPERATION_ARGSENTRY']._serialized_start=2926
-  _globals['_OPERATION_ARGSENTRY']._serialized_end=2994
-  _globals['_GATE']._serialized_start=3019
-  _globals['_GATE']._serialized_end=3037
-  _globals['_QUBIT']._serialized_start=3039
-  _globals['_QUBIT']._serialized_end=3058
-  _globals['_ARG']._serialized_start=3061
-  _globals['_ARG']._serialized_end=3217
-  _globals['_ARGVALUE']._serialized_start=3220
-  _globals['_ARGVALUE']._serialized_end=3555
-  _globals['_REPEATEDINT64']._serialized_start=3557
-  _globals['_REPEATEDINT64']._serialized_end=3588
-  _globals['_REPEATEDDOUBLE']._serialized_start=3590
-  _globals['_REPEATEDDOUBLE']._serialized_end=3622
-  _globals['_REPEATEDSTRING']._serialized_start=3624
-  _globals['_REPEATEDSTRING']._serialized_end=3656
-  _globals['_REPEATEDBOOLEAN']._serialized_start=3658
-  _globals['_REPEATEDBOOLEAN']._serialized_end=3691
-  _globals['_ARGFUNCTION']._serialized_start=3693
-  _globals['_ARGFUNCTION']._serialized_end=3759
-  _globals['_CIRCUITOPERATION']._serialized_start=3762
-  _globals['_CIRCUITOPERATION']._serialized_end=4065
-  _globals['_REPETITIONSPECIFICATION']._serialized_start=4068
-  _globals['_REPETITIONSPECIFICATION']._serialized_end=4256
-  _globals['_REPETITIONSPECIFICATION_REPETITIONIDS']._serialized_start=4208
-  _globals['_REPETITIONSPECIFICATION_REPETITIONIDS']._serialized_end=4236
-  _globals['_QUBITMAPPING']._serialized_start=4259
-  _globals['_QUBITMAPPING']._serialized_end=4431
-  _globals['_QUBITMAPPING_QUBITENTRY']._serialized_start=4337
-  _globals['_QUBITMAPPING_QUBITENTRY']._serialized_end=4431
-  _globals['_MEASUREMENTKEY']._serialized_start=4433
-  _globals['_MEASUREMENTKEY']._serialized_end=4469
-  _globals['_MEASUREMENTKEYMAPPING']._serialized_start=4472
-  _globals['_MEASUREMENTKEYMAPPING']._serialized_end=4698
-  _globals['_MEASUREMENTKEYMAPPING_MEASUREMENTKEYENTRY']._serialized_start=4577
-  _globals['_MEASUREMENTKEYMAPPING_MEASUREMENTKEYENTRY']._serialized_end=4698
-  _globals['_ARGMAPPING']._serialized_start=4701
-  _globals['_ARGMAPPING']._serialized_end=4861
-  _globals['_ARGMAPPING_ARGENTRY']._serialized_start=4773
-  _globals['_ARGMAPPING_ARGENTRY']._serialized_end=4861
-  _globals['_INTERNALGATE']._serialized_start=4864
-  _globals['_INTERNALGATE']._serialized_end=5069
-  _globals['_INTERNALGATE_GATEARGSENTRY']._serialized_start=4997
-  _globals['_INTERNALGATE_GATEARGSENTRY']._serialized_end=5069
-  _globals['_COUPLERPULSEGATE']._serialized_start=5072
-  _globals['_COUPLERPULSEGATE']._serialized_end=5544
+  _globals['_OPERATION']._serialized_end=3205
+  _globals['_OPERATION_ARGSENTRY']._serialized_start=3114
+  _globals['_OPERATION_ARGSENTRY']._serialized_end=3182
+  _globals['_GATE']._serialized_start=3207
+  _globals['_GATE']._serialized_end=3225
+  _globals['_QUBIT']._serialized_start=3227
+  _globals['_QUBIT']._serialized_end=3246
+  _globals['_ARG']._serialized_start=3249
+  _globals['_ARG']._serialized_end=3405
+  _globals['_ARGVALUE']._serialized_start=3408
+  _globals['_ARGVALUE']._serialized_end=3743
+  _globals['_REPEATEDINT64']._serialized_start=3745
+  _globals['_REPEATEDINT64']._serialized_end=3776
+  _globals['_REPEATEDDOUBLE']._serialized_start=3778
+  _globals['_REPEATEDDOUBLE']._serialized_end=3810
+  _globals['_REPEATEDSTRING']._serialized_start=3812
+  _globals['_REPEATEDSTRING']._serialized_end=3844
+  _globals['_REPEATEDBOOLEAN']._serialized_start=3846
+  _globals['_REPEATEDBOOLEAN']._serialized_end=3879
+  _globals['_ARGFUNCTION']._serialized_start=3881
+  _globals['_ARGFUNCTION']._serialized_end=3947
+  _globals['_CIRCUITOPERATION']._serialized_start=3950
+  _globals['_CIRCUITOPERATION']._serialized_end=4253
+  _globals['_REPETITIONSPECIFICATION']._serialized_start=4256
+  _globals['_REPETITIONSPECIFICATION']._serialized_end=4444
+  _globals['_REPETITIONSPECIFICATION_REPETITIONIDS']._serialized_start=4396
+  _globals['_REPETITIONSPECIFICATION_REPETITIONIDS']._serialized_end=4424
+  _globals['_QUBITMAPPING']._serialized_start=4447
+  _globals['_QUBITMAPPING']._serialized_end=4619
+  _globals['_QUBITMAPPING_QUBITENTRY']._serialized_start=4525
+  _globals['_QUBITMAPPING_QUBITENTRY']._serialized_end=4619
+  _globals['_MEASUREMENTKEY']._serialized_start=4621
+  _globals['_MEASUREMENTKEY']._serialized_end=4657
+  _globals['_MEASUREMENTKEYMAPPING']._serialized_start=4660
+  _globals['_MEASUREMENTKEYMAPPING']._serialized_end=4886
+  _globals['_MEASUREMENTKEYMAPPING_MEASUREMENTKEYENTRY']._serialized_start=4765
+  _globals['_MEASUREMENTKEYMAPPING_MEASUREMENTKEYENTRY']._serialized_end=4886
+  _globals['_ARGMAPPING']._serialized_start=4889
+  _globals['_ARGMAPPING']._serialized_end=5049
+  _globals['_ARGMAPPING_ARGENTRY']._serialized_start=4961
+  _globals['_ARGMAPPING_ARGENTRY']._serialized_end=5049
+  _globals['_INTERNALGATE']._serialized_start=5052
+  _globals['_INTERNALGATE']._serialized_end=5257
+  _globals['_INTERNALGATE_GATEARGSENTRY']._serialized_start=5185
+  _globals['_INTERNALGATE_GATEARGSENTRY']._serialized_end=5257
+  _globals['_COUPLERPULSEGATE']._serialized_start=5260
+  _globals['_COUPLERPULSEGATE']._serialized_end=5732
+  _globals['_CLIFFORDTABLEAU']._serialized_start=5735
+  _globals['_CLIFFORDTABLEAU']._serialized_end=5874
+  _globals['_SINGLEQUBITCLIFFORDGATE']._serialized_start=5876
+  _globals['_SINGLEQUBITCLIFFORDGATE']._serialized_end=5955
+  _globals['_IDENTITYGATE']._serialized_start=5957
+  _globals['_IDENTITYGATE']._serialized_end=5990
+  _globals['_HPOWGATE']._serialized_start=5992
+  _globals['_HPOWGATE']._serialized_end=6050
 # @@protoc_insertion_point(module_scope)
diff --git a/cirq-google/cirq_google/serialization/arg_func_langs.py b/cirq-google/cirq_google/serialization/arg_func_langs.py
--- a/cirq-google/cirq_google/serialization/arg_func_langs.py
+++ b/cirq-google/cirq_google/serialization/arg_func_langs.py
@@ -19,6 +19,7 @@
 import sympy
 from cirq_google.api import v2
 from cirq_google.ops import InternalGate
+from cirq.qis import CliffordTableau
 
 SUPPORTED_FUNCTIONS_FOR_LANGUAGE: Dict[Optional[str], FrozenSet[str]] = {
     '': frozenset(),
@@ -455,3 +456,45 @@ def internal_gate_from_proto(
         num_qubits=int(msg.num_qubits),
         **gate_args,
     )
+
+
+def clifford_tableau_arg_to_proto(
+    value: CliffordTableau, *, out: Optional[v2.program_pb2.CliffordTableau] = None
+):
+    """Writes an CliffordTableau object into an CliffordTableau proto.
+    Args:
+        value: The gate to encode.
+        arg_function_language: The language to use when encoding functions. If
+            this is set to None, it will be set to the minimal language
+            necessary to support the features that were actually used.
+        out: The proto to write the result into. Defaults to a new instance.
+    Returns:
+        The proto that was written into.
+    """
+    msg = v2.program_pb2.CliffordTableau() if out is None else out
+    msg.num_qubits = value.n
+    msg.initial_state = value.initial_state
+    msg.xs.extend(value.xs.flatten())
+    msg.rs.extend(value.rs.flatten())
+    msg.zs.extend(value.zs.flatten())
+    return msg
+
+
+def clifford_tableau_from_proto(
+    msg: v2.program_pb2.CliffordTableau, arg_function_language: str
+) -> CliffordTableau:
+    """Extracts a CliffordTableau object from a CliffordTableau proto.
+    Args:
+        msg: The proto containing a serialized value.
+        arg_function_language: The `arg_function_language` field from
+            `Program.Language`.
+    Returns:
+        The deserialized InternalGate object.
+    """
+    return CliffordTableau(
+        num_qubits=msg.num_qubits,
+        initial_state=msg.initial_state,
+        rs=np.array(msg.rs, dtype=bool) if msg.rs else None,
+        xs=np.array(msg.xs, dtype=bool).reshape((2 * msg.num_qubits, -1)) if msg.xs else None,
+        zs=np.array(msg.zs, dtype=bool).reshape((2 * msg.num_qubits, -1)) if msg.zs else None,
+    )
diff --git a/cirq-google/cirq_google/serialization/circuit_serializer.py b/cirq-google/cirq_google/serialization/circuit_serializer.py
--- a/cirq-google/cirq_google/serialization/circuit_serializer.py
+++ b/cirq-google/cirq_google/serialization/circuit_serializer.py
@@ -176,9 +176,7 @@ def _serialize_gate_op(
                 out=msg.phasedxpowgate.exponent,
                 arg_function_language=arg_function_language,
             )
-        elif isinstance(gate, (cirq.PhasedXZGate, cirq.ops.SingleQubitCliffordGate)):
-            if isinstance(gate, cirq.ops.SingleQubitCliffordGate):
-                gate = gate.to_phased_xz_gate()
+        elif isinstance(gate, cirq.PhasedXZGate):
             arg_func_langs.float_arg_to_proto(
                 gate.x_exponent,
                 out=msg.phasedxzgate.x_exponent,
@@ -194,6 +192,18 @@ def _serialize_gate_op(
                 out=msg.phasedxzgate.axis_phase_exponent,
                 arg_function_language=arg_function_language,
             )
+        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,
+                arg_function_language=arg_function_language,
+            )
         elif isinstance(gate, cirq.CZPowGate):
             arg_func_langs.float_arg_to_proto(
                 gate.exponent,
@@ -491,6 +501,23 @@ def _deserialize_gate_op(
             )(*qubits)
             if operation_proto.zpowgate.is_physical_z:
                 op = op.with_tags(PhysicalZTag())
+        elif which_gate_type == 'hpowgate':
+            op = cirq.HPowGate(
+                exponent=arg_func_langs.float_arg_from_proto(
+                    operation_proto.hpowgate.exponent,
+                    arg_function_language=arg_function_language,
+                    required_arg_name=None,
+                )
+                or 0.0
+            )(*qubits)
+        elif which_gate_type == 'identitygate':
+            op = cirq.IdentityGate(qid_shape=tuple(operation_proto.identitygate.qid_shape))(*qubits)
+        elif which_gate_type == 'singlequbitcliffordgate':
+            tableau = arg_func_langs.clifford_tableau_from_proto(
+                operation_proto.singlequbitcliffordgate.tableau,
+                arg_function_language=arg_function_language,
+            )
+            op = cirq.ops.SingleQubitCliffordGate.from_clifford_tableau(tableau)(*qubits)
         elif which_gate_type == 'phasedxpowgate':
             exponent = (
                 arg_func_langs.float_arg_from_proto(


Test patch:
diff --git a/cirq-core/cirq/testing/__init__.py b/cirq-core/cirq/testing/__init__.py
--- a/cirq-core/cirq/testing/__init__.py
+++ b/cirq-core/cirq/testing/__init__.py
@@ -69,7 +69,12 @@
 
 from cirq.testing.equivalent_repr_eval import assert_equivalent_repr
 
-from cirq.testing.gate_features import SingleQubitGate, TwoQubitGate, ThreeQubitGate
+from cirq.testing.gate_features import (
+    SingleQubitGate,
+    TwoQubitGate,
+    ThreeQubitGate,
+    DoesNotSupportSerializationGate,
+)
 
 from cirq.testing.json import assert_json_roundtrip_works
 
diff --git a/cirq-core/cirq/testing/gate_features.py b/cirq-core/cirq/testing/gate_features.py
--- a/cirq-core/cirq/testing/gate_features.py
+++ b/cirq-core/cirq/testing/gate_features.py
@@ -36,3 +36,13 @@ class ThreeQubitGate(raw_types.Gate):
 
     def _num_qubits_(self) -> int:
         return 3
+
+
+class DoesNotSupportSerializationGate(raw_types.Gate):
+    """A gate that can't be serialized."""
+
+    def __init__(self, n_qubits: int = 1):
+        self.n_qubits = n_qubits
+
+    def _num_qubits_(self) -> int:
+        return self.n_qubits
diff --git a/cirq-google/cirq_google/devices/grid_device_test.py b/cirq-google/cirq_google/devices/grid_device_test.py
--- a/cirq-google/cirq_google/devices/grid_device_test.py
+++ b/cirq-google/cirq_google/devices/grid_device_test.py
@@ -387,7 +387,9 @@ def test_grid_device_validate_operations_negative():
         device.validate_operation(cirq.CZ(q00, q10))
 
     with pytest.raises(ValueError, match='gate which is not supported'):
-        device.validate_operation(cirq.H(device_info.grid_qubits[0]))
+        device.validate_operation(
+            cirq.testing.DoesNotSupportSerializationGate()(device_info.grid_qubits[0])
+        )
 
 
 @pytest.mark.parametrize(
diff --git a/cirq-google/cirq_google/engine/engine_processor_test.py b/cirq-google/cirq_google/engine/engine_processor_test.py
--- a/cirq-google/cirq_google/engine/engine_processor_test.py
+++ b/cirq-google/cirq_google/engine/engine_processor_test.py
@@ -288,7 +288,9 @@ def test_get_device():
     with pytest.raises(ValueError):
         device.validate_operation(cirq.X(cirq.GridQubit(1, 2)))
     with pytest.raises(ValueError):
-        device.validate_operation(cirq.H(cirq.GridQubit(0, 0)))
+        device.validate_operation(
+            cirq.testing.DoesNotSupportSerializationGate()(cirq.GridQubit(0, 0))
+        )
     with pytest.raises(ValueError):
         device.validate_operation(cirq.CZ(cirq.GridQubit(1, 1), cirq.GridQubit(2, 2)))
 
diff --git a/cirq-google/cirq_google/engine/engine_test.py b/cirq-google/cirq_google/engine/engine_test.py
--- a/cirq-google/cirq_google/engine/engine_test.py
+++ b/cirq-google/cirq_google/engine/engine_test.py
@@ -824,6 +824,8 @@ def test_get_engine_device(get_processor):
     with pytest.raises(ValueError):
         device.validate_operation(cirq.X(cirq.GridQubit(1, 2)))
     with pytest.raises(ValueError):
-        device.validate_operation(cirq.H(cirq.GridQubit(0, 0)))
+        device.validate_operation(
+            cirq.testing.DoesNotSupportSerializationGate()(cirq.GridQubit(0, 0))
+        )
     with pytest.raises(ValueError):
         device.validate_operation(cirq.CZ(cirq.GridQubit(1, 1), cirq.GridQubit(2, 2)))
diff --git a/cirq-google/cirq_google/engine/virtual_engine_factory_test.py b/cirq-google/cirq_google/engine/virtual_engine_factory_test.py
--- a/cirq-google/cirq_google/engine/virtual_engine_factory_test.py
+++ b/cirq-google/cirq_google/engine/virtual_engine_factory_test.py
@@ -35,7 +35,9 @@ def _test_processor(processor: cg.engine.abstract_processor.AbstractProcessor):
     circuit = cirq.Circuit(cirq.X(bad_qubit), cirq.measure(bad_qubit))
     with pytest.raises(ValueError, match='Qubit not on device'):
         _ = processor.run(circuit, repetitions=100)
-    circuit = cirq.Circuit(cirq.H(good_qubit), cirq.measure(good_qubit))
+    circuit = cirq.Circuit(
+        cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
+    )
     with pytest.raises(ValueError, match='Cannot serialize op'):
         _ = processor.run(circuit, repetitions=100)
 
@@ -195,7 +197,9 @@ def test_create_default_noisy_quantum_virtual_machine():
         with pytest.raises(ValueError, match='Qubit not on device'):
             _ = processor.run(circuit, repetitions=100)
         good_qubit = cirq.GridQubit(5, 4)
-        circuit = cirq.Circuit(cirq.H(good_qubit), cirq.measure(good_qubit))
+        circuit = cirq.Circuit(
+            cirq.testing.DoesNotSupportSerializationGate()(good_qubit), cirq.measure(good_qubit)
+        )
         with pytest.raises(ValueError, match='.* contains a gate which is not supported.'):
             _ = processor.run(circuit, repetitions=100)
         device_specification = processor.get_device_specification()
diff --git a/cirq-google/cirq_google/serialization/arg_func_langs_test.py b/cirq-google/cirq_google/serialization/arg_func_langs_test.py
--- a/cirq-google/cirq_google/serialization/arg_func_langs_test.py
+++ b/cirq-google/cirq_google/serialization/arg_func_langs_test.py
@@ -28,8 +28,11 @@
     internal_gate_from_proto,
     ARG_LIKE,
     LANGUAGE_ORDER,
+    clifford_tableau_arg_to_proto,
+    clifford_tableau_from_proto,
 )
 from cirq_google.api import v2
+from cirq.qis import CliffordTableau
 
 
 @pytest.mark.parametrize(
@@ -249,3 +252,27 @@ def test_invalid_list():
 
     with pytest.raises(ValueError):
         _ = arg_to_proto([1.0, ''])
+
+
+@pytest.mark.parametrize('lang', LANGUAGE_ORDER)
+def test_clifford_tableau(lang):
+    tests = [
+        CliffordTableau(
+            1,
+            0,
+            rs=np.array([True, False], dtype=bool),
+            xs=np.array([[True], [False]], dtype=bool),
+            zs=np.array([[True], [False]], dtype=bool),
+        ),
+        CliffordTableau(
+            1,
+            1,
+            rs=np.array([True, True], dtype=bool),
+            xs=np.array([[True], [False]], dtype=bool),
+            zs=np.array([[False], [False]], dtype=bool),
+        ),
+    ]
+    for ct in tests:
+        proto = clifford_tableau_arg_to_proto(ct)
+        tableau = clifford_tableau_from_proto(proto, lang)
+        assert tableau == ct
diff --git a/cirq-google/cirq_google/serialization/circuit_serializer_test.py b/cirq-google/cirq_google/serialization/circuit_serializer_test.py
--- a/cirq-google/cirq_google/serialization/circuit_serializer_test.py
+++ b/cirq-google/cirq_google/serialization/circuit_serializer_test.py
@@ -279,6 +279,28 @@ def circuit_proto(json: Dict, qubits: List[str]):
             }
         ),
     ),
+    (
+        cirq.ops.SingleQubitCliffordGate.X(Q0),
+        op_proto(
+            {
+                'singlequbitcliffordgate': {
+                    'tableau': {
+                        'num_qubits': 1,
+                        'initial_state': 0,
+                        'rs': [False, True],
+                        'xs': [True, False],
+                        'zs': [False, True],
+                    }
+                },
+                'qubit_constant_index': [0],
+            }
+        ),
+    ),
+    (
+        cirq.H(Q0),
+        op_proto({'hpowgate': {'exponent': {'float_value': 1.0}}, 'qubit_constant_index': [0]}),
+    ),
+    (cirq.I(Q0), op_proto({'identitygate': {'qid_shape': [2]}, 'qubit_constant_index': [0]})),
 ]
 
 
@@ -698,12 +720,8 @@ def test_circuit_with_cliffords():
     circuit = cirq.Circuit(
         g(q) for g in cirq.ops.SingleQubitCliffordGate.all_single_qubit_cliffords
     )
-    want = cirq.Circuit(
-        g.to_phased_xz_gate()(q)
-        for g in cirq.ops.SingleQubitCliffordGate.all_single_qubit_cliffords
-    )
     msg = cg.CIRCUIT_SERIALIZER.serialize(circuit)
-    assert cg.CIRCUIT_SERIALIZER.deserialize(msg) == want
+    assert cg.CIRCUIT_SERIALIZER.deserialize(msg) == circuit
 
 
 def test_circuit_with_couplerpulse():