Skip to content

Commit

Permalink
(De-)serialization of CircuitOperations (#3923)
Browse files Browse the repository at this point in the history
Follow up for #3891. Part of #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
  • Loading branch information
95-martin-orion committed May 24, 2021
1 parent 0c0366b commit 71285d2
Show file tree
Hide file tree
Showing 12 changed files with 1,352 additions and 148 deletions.
2 changes: 2 additions & 0 deletions cirq-google/cirq_google/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,13 @@
)

from cirq_google.op_deserializer import (
CircuitOpDeserializer,
DeserializingArg,
GateOpDeserializer,
)

from cirq_google.op_serializer import (
CircuitOpSerializer,
GateOpSerializer,
SerializingArg,
)
Expand Down
11 changes: 11 additions & 0 deletions cirq-google/cirq_google/common_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,11 @@ def _can_serialize_limited_iswap(exponent: float):
op_wrapper=lambda op, proto: _add_phase_match(op, proto),
)

#############################################
#
# Miscellaneous serializers and deserializers
#
#############################################

#
# WaitGate serializer and deserializer
Expand Down Expand Up @@ -675,3 +680,9 @@ def _can_serialize_limited_iswap(exponent: float):
],
num_qubits_param='num_qubits',
)

#
# CircuitOperation serializer and deserializer
#
CIRCUIT_OP_SERIALIZER = op_serializer.CircuitOpSerializer()
CIRCUIT_OP_DESERIALIZER = op_deserializer.CircuitOpDeserializer()
14 changes: 10 additions & 4 deletions cirq-google/cirq_google/devices/known_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from cirq._doc import document
from cirq.devices import GridQubit
from cirq_google import gate_sets, serializable_gate_set
from cirq_google import gate_sets, op_serializer, serializable_gate_set
from cirq_google.api import v2
from cirq_google.api.v2 import device_pb2
from cirq_google.devices.serializable_device import SerializableDevice
Expand Down Expand Up @@ -132,9 +132,9 @@ def create_device_proto_for_qubits(
gs_proto = out.valid_gate_sets.add()
gs_proto.name = gate_set.gate_set_name
gate_ids: Set[str] = set()
for gate_type in gate_set.serializers:
for serializer in gate_set.serializers[gate_type]:
gate_id = serializer.serialized_gate_id
for internal_type in gate_set.serializers:
for serializer in gate_set.serializers[internal_type]:
gate_id = serializer.serialized_id
if gate_id in gate_ids:
# Only add each type once
continue
Expand All @@ -143,7 +143,13 @@ def create_device_proto_for_qubits(
gate = gs_proto.valid_gates.add()
gate.id = gate_id

if not isinstance(serializer, op_serializer.GateOpSerializer):
# This implies that 'serializer' handles non-gate ops,
# such as CircuitOperations. No other properties apply.
continue

# Choose target set and number of qubits based on gate type.
gate_type = internal_type

# Note: if it is not a measurement gate and doesn't inherit
# from SingleQubitGate, it's assumed to be a two qubit gate.
Expand Down
86 changes: 86 additions & 0 deletions cirq-google/cirq_google/devices/known_devices_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,40 @@ def test_sycamore_devices(device):
assert device.duration_of(sqrt_iswap) == cirq.Duration(nanos=32)


def test_sycamore_circuitop_device():
circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='circuitop_gateset',
serializers=[cgc.CIRCUIT_OP_SERIALIZER],
deserializers=[cgc.CIRCUIT_OP_DESERIALIZER],
)
gateset_list = [
cirq.google.gate_sets.SQRT_ISWAP_GATESET,
cirq.google.gate_sets.SYC_GATESET,
circuitop_gateset,
]
circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
known_devices._SYCAMORE23_GRID,
gateset_list,
known_devices._SYCAMORE_DURATIONS_PICOS,
)
device = cirq.google.SerializableDevice.from_proto(
proto=circuitop_proto,
gate_sets=gateset_list,
)
q0 = cirq.GridQubit(5, 3)
q1 = cirq.GridQubit(5, 4)
syc = cirq.FSimGate(theta=np.pi / 2, phi=np.pi / 6)(q0, q1)
sqrt_iswap = cirq.FSimGate(theta=np.pi / 4, phi=0)(q0, q1)
circuit_op = cirq.CircuitOperation(cirq.FrozenCircuit(syc, sqrt_iswap))
device.validate_operation(syc)
device.validate_operation(sqrt_iswap)
device.validate_operation(circuit_op)
assert device.duration_of(syc) == cirq.Duration(nanos=12)
assert device.duration_of(sqrt_iswap) == cirq.Duration(nanos=32)
# CircuitOperations don't have a set duration.
assert device.duration_of(circuit_op) == cirq.Duration(nanos=0)


def test_sycamore_grid_layout():
# Qubits on Sycamore but not on Sycamore23
q0 = cirq.GridQubit(5, 5)
Expand All @@ -498,6 +532,58 @@ def test_sycamore_grid_layout():
cirq_google.Sycamore23.validate_operation(sqrt_iswap)


def test_proto_with_circuitop():
circuitop_gateset = cirq.google.serializable_gate_set.SerializableGateSet(
gate_set_name='circuitop_gateset',
serializers=[cgc.CIRCUIT_OP_SERIALIZER],
deserializers=[cgc.CIRCUIT_OP_DESERIALIZER],
)
circuitop_proto = cirq.google.devices.known_devices.create_device_proto_from_diagram(
"aa\naa",
[circuitop_gateset],
)

assert (
str(circuitop_proto)
== """\
valid_gate_sets {
name: "circuitop_gateset"
valid_gates {
id: "circuit"
}
}
valid_qubits: "0_0"
valid_qubits: "0_1"
valid_qubits: "1_0"
valid_qubits: "1_1"
valid_targets {
name: "meas_targets"
target_ordering: SUBSET_PERMUTATION
}
valid_targets {
name: "2_qubit_targets"
target_ordering: SYMMETRIC
targets {
ids: "0_0"
ids: "0_1"
}
targets {
ids: "0_0"
ids: "1_0"
}
targets {
ids: "0_1"
ids: "1_1"
}
targets {
ids: "1_0"
ids: "1_1"
}
}
"""
)


def test_proto_with_waitgate():
wait_gateset = cirq_google.serializable_gate_set.SerializableGateSet(
gate_set_name='wait_gateset',
Expand Down
24 changes: 15 additions & 9 deletions cirq-google/cirq_google/devices/serializable_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,20 +169,20 @@ def from_proto(
# Loop through serializers and map gate_definitions to type
gates_by_type: Dict[Type['cirq.Gate'], List[_GateDefinition]] = {}
for gate_set in gate_sets:
for gate_type in gate_set.supported_gate_types():
for serializer in gate_set.serializers[gate_type]:
gate_id = serializer.serialized_gate_id
if gate_id not in gate_definitions:
for internal_type in gate_set.supported_internal_types():
for serializer in gate_set.serializers[internal_type]:
serialized_id = serializer.serialized_id
if serialized_id not in gate_definitions:
raise ValueError(
f'Serializer has {gate_id} which is not supported '
f'Serializer has {serialized_id} which is not supported '
'by the device specification'
)
if gate_type not in gates_by_type:
gates_by_type[gate_type] = []
gate_def = gate_definitions[gate_id].with_can_serialize_predicate(
if internal_type not in gates_by_type:
gates_by_type[internal_type] = []
gate_def = gate_definitions[serialized_id].with_can_serialize_predicate(
serializer.can_serialize_predicate
)
gates_by_type[gate_type].append(gate_def)
gates_by_type[internal_type].append(gate_def)

return SerializableDevice(
qubits=[cls._qid_from_str(q) for q in proto.valid_qubits],
Expand Down Expand Up @@ -264,6 +264,12 @@ def _find_operation_type(self, op: 'cirq.Operation') -> Optional[_GateDefinition
the value corresponding to that key or None if no type matches
"""
for type_key, gate_defs in self.gate_definitions.items():
if type_key == circuits.FrozenCircuit and isinstance(
op.untagged, circuits.CircuitOperation
):
for gate_def in gate_defs:
if gate_def.can_serialize_predicate(op):
return gate_def
if isinstance(op.gate, type_key):
for gate_def in gate_defs:
if gate_def.can_serialize_predicate(op):
Expand Down
2 changes: 2 additions & 0 deletions cirq-google/cirq_google/json_test_data/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
],
should_not_be_serialized=[
'AnnealSequenceSearchStrategy',
'CircuitOpDeserializer',
'CircuitOpSerializer',
'CircuitWithCalibration',
'ConvertToSqrtIswapGates',
'ConvertToSycamoreGates',
Expand Down

0 comments on commit 71285d2

Please sign in to comment.