Skip to content

Commit e59d5cc

Browse files
95-martin-orionrht
authored andcommitted
(De-)serialization of CircuitOperations (quantumlib#3923)
Follow up for quantumlib#3891. Part of quantumlib#3634. This PR adds the `CircuitOp(De)serializer` classes and updates existing behavior to handle them appropriately, including: - Addition of `Op(De)serializer` superclasses for operation (de-)serializers - Backwards-compatible conversion of `GateOp(De)serializer` fields to non-public, with public properties - Updates and deprecations for names made awkward by this change (e.g. `gate_type` -> `internal_type`) Changes **not** included in this PR: - Add `CircuitOperation` (de-)serializers to the default QCS gateset
1 parent cbdf90b commit e59d5cc

12 files changed

+1352
-148
lines changed

cirq-google/cirq_google/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@
112112
)
113113

114114
from cirq_google.op_deserializer import (
115+
CircuitOpDeserializer,
115116
DeserializingArg,
116117
GateOpDeserializer,
117118
)
118119

119120
from cirq_google.op_serializer import (
121+
CircuitOpSerializer,
120122
GateOpSerializer,
121123
SerializingArg,
122124
)

cirq-google/cirq_google/common_serializers.py

+11
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,11 @@ def _can_serialize_limited_iswap(exponent: float):
646646
op_wrapper=lambda op, proto: _add_phase_match(op, proto),
647647
)
648648

649+
#############################################
650+
#
651+
# Miscellaneous serializers and deserializers
652+
#
653+
#############################################
649654

650655
#
651656
# WaitGate serializer and deserializer
@@ -675,3 +680,9 @@ def _can_serialize_limited_iswap(exponent: float):
675680
],
676681
num_qubits_param='num_qubits',
677682
)
683+
684+
#
685+
# CircuitOperation serializer and deserializer
686+
#
687+
CIRCUIT_OP_SERIALIZER = op_serializer.CircuitOpSerializer()
688+
CIRCUIT_OP_DESERIALIZER = op_deserializer.CircuitOpDeserializer()

cirq-google/cirq_google/devices/known_devices.py

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
from cirq._doc import document
1818
from cirq.devices import GridQubit
19-
from cirq_google import gate_sets, serializable_gate_set
19+
from cirq_google import gate_sets, op_serializer, serializable_gate_set
2020
from cirq_google.api import v2
2121
from cirq_google.api.v2 import device_pb2
2222
from cirq_google.devices.serializable_device import SerializableDevice
@@ -132,9 +132,9 @@ def create_device_proto_for_qubits(
132132
gs_proto = out.valid_gate_sets.add()
133133
gs_proto.name = gate_set.gate_set_name
134134
gate_ids: Set[str] = set()
135-
for gate_type in gate_set.serializers:
136-
for serializer in gate_set.serializers[gate_type]:
137-
gate_id = serializer.serialized_gate_id
135+
for internal_type in gate_set.serializers:
136+
for serializer in gate_set.serializers[internal_type]:
137+
gate_id = serializer.serialized_id
138138
if gate_id in gate_ids:
139139
# Only add each type once
140140
continue
@@ -143,7 +143,13 @@ def create_device_proto_for_qubits(
143143
gate = gs_proto.valid_gates.add()
144144
gate.id = gate_id
145145

146+
if not isinstance(serializer, op_serializer.GateOpSerializer):
147+
# This implies that 'serializer' handles non-gate ops,
148+
# such as CircuitOperations. No other properties apply.
149+
continue
150+
146151
# Choose target set and number of qubits based on gate type.
152+
gate_type = internal_type
147153

148154
# Note: if it is not a measurement gate and doesn't inherit
149155
# from SingleQubitGate, it's assumed to be a two qubit gate.

cirq-google/cirq_google/devices/known_devices_test.py

+86
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,40 @@ def test_sycamore_devices(device):
483483
assert device.duration_of(sqrt_iswap) == cirq.Duration(nanos=32)
484484

485485

486+
def test_sycamore_circuitop_device():
487+
circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet(
488+
gate_set_name='circuitop_gateset',
489+
serializers=[cgc.CIRCUIT_OP_SERIALIZER],
490+
deserializers=[cgc.CIRCUIT_OP_DESERIALIZER],
491+
)
492+
gateset_list = [
493+
cirq.google.gate_sets.SQRT_ISWAP_GATESET,
494+
cirq.google.gate_sets.SYC_GATESET,
495+
circuitop_gateset,
496+
]
497+
circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
498+
known_devices._SYCAMORE23_GRID,
499+
gateset_list,
500+
known_devices._SYCAMORE_DURATIONS_PICOS,
501+
)
502+
device = cirq.google.SerializableDevice.from_proto(
503+
proto=circuitop_proto,
504+
gate_sets=gateset_list,
505+
)
506+
q0 = cirq.GridQubit(5, 3)
507+
q1 = cirq.GridQubit(5, 4)
508+
syc = cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6)(q0, q1)
509+
sqrt_iswap = cirq.FSimGate(theta=np.pi / 4, phi=0)(q0, q1)
510+
circuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(syc, sqrt_iswap))
511+
device.validate_operation(syc)
512+
device.validate_operation(sqrt_iswap)
513+
device.validate_operation(circuit_op)
514+
assert device.duration_of(syc) == cirq.Duration(nanos=12)
515+
assert device.duration_of(sqrt_iswap) == cirq.Duration(nanos=32)
516+
# CircuitOperations don't have a set duration.
517+
assert device.duration_of(circuit_op) == cirq.Duration(nanos=0)
518+
519+
486520
def test_sycamore_grid_layout():
487521
# Qubits on Sycamore but not on Sycamore23
488522
q0 = cirq.GridQubit(5, 5)
@@ -498,6 +532,58 @@ def test_sycamore_grid_layout():
498532
cirq_google.Sycamore23.validate_operation(sqrt_iswap)
499533

500534

535+
def test_proto_with_circuitop():
536+
circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet(
537+
gate_set_name='circuitop_gateset',
538+
serializers=[cgc.CIRCUIT_OP_SERIALIZER],
539+
deserializers=[cgc.CIRCUIT_OP_DESERIALIZER],
540+
)
541+
circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
542+
"aa\naa",
543+
[circuitop_gateset],
544+
)
545+
546+
assert (
547+
str(circuitop_proto)
548+
== """\
549+
valid_gate_sets {
550+
name: "circuitop_gateset"
551+
valid_gates {
552+
id: "circuit"
553+
}
554+
}
555+
valid_qubits: "0_0"
556+
valid_qubits: "0_1"
557+
valid_qubits: "1_0"
558+
valid_qubits: "1_1"
559+
valid_targets {
560+
name: "meas_targets"
561+
target_ordering: SUBSET_PERMUTATION
562+
}
563+
valid_targets {
564+
name: "2_qubit_targets"
565+
target_ordering: SYMMETRIC
566+
targets {
567+
ids: "0_0"
568+
ids: "0_1"
569+
}
570+
targets {
571+
ids: "0_0"
572+
ids: "1_0"
573+
}
574+
targets {
575+
ids: "0_1"
576+
ids: "1_1"
577+
}
578+
targets {
579+
ids: "1_0"
580+
ids: "1_1"
581+
}
582+
}
583+
"""
584+
)
585+
586+
501587
def test_proto_with_waitgate():
502588
wait_gateset = cirq_google.serializable_gate_set.SerializableGateSet(
503589
gate_set_name='wait_gateset',

cirq-google/cirq_google/devices/serializable_device.py

+15-9
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,20 @@ def from_proto(
169169
# Loop through serializers and map gate_definitions to type
170170
gates_by_type: Dict[Type['cirq.Gate'], List[_GateDefinition]] = {}
171171
for gate_set in gate_sets:
172-
for gate_type in gate_set.supported_gate_types():
173-
for serializer in gate_set.serializers[gate_type]:
174-
gate_id = serializer.serialized_gate_id
175-
if gate_id not in gate_definitions:
172+
for internal_type in gate_set.supported_internal_types():
173+
for serializer in gate_set.serializers[internal_type]:
174+
serialized_id = serializer.serialized_id
175+
if serialized_id not in gate_definitions:
176176
raise ValueError(
177-
f'Serializer has {gate_id} which is not supported '
177+
f'Serializer has {serialized_id} which is not supported '
178178
'by the device specification'
179179
)
180-
if gate_type not in gates_by_type:
181-
gates_by_type[gate_type] = []
182-
gate_def = gate_definitions[gate_id].with_can_serialize_predicate(
180+
if internal_type not in gates_by_type:
181+
gates_by_type[internal_type] = []
182+
gate_def = gate_definitions[serialized_id].with_can_serialize_predicate(
183183
serializer.can_serialize_predicate
184184
)
185-
gates_by_type[gate_type].append(gate_def)
185+
gates_by_type[internal_type].append(gate_def)
186186

187187
return SerializableDevice(
188188
qubits=[cls._qid_from_str(q) for q in proto.valid_qubits],
@@ -264,6 +264,12 @@ def _find_operation_type(self, op: 'cirq.Operation') -> Optional[_GateDefinition
264264
the value corresponding to that key or None if no type matches
265265
"""
266266
for type_key, gate_defs in self.gate_definitions.items():
267+
if type_key == circuits.FrozenCircuit and isinstance(
268+
op.untagged, circuits.CircuitOperation
269+
):
270+
for gate_def in gate_defs:
271+
if gate_def.can_serialize_predicate(op):
272+
return gate_def
267273
if isinstance(op.gate, type_key):
268274
for gate_def in gate_defs:
269275
if gate_def.can_serialize_predicate(op):

cirq-google/cirq_google/json_test_data/spec.py

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
],
2626
should_not_be_serialized=[
2727
'AnnealSequenceSearchStrategy',
28+
'CircuitOpDeserializer',
29+
'CircuitOpSerializer',
2830
'CircuitWithCalibration',
2931
'ConvertToSqrtIswapGates',
3032
'ConvertToSycamoreGates',

0 commit comments

Comments
 (0)