From e308f016c80d69b76fd80187195dbea32a3d4b25 Mon Sep 17 00:00:00 2001 From: Cheng Xing Date: Fri, 17 Jun 2022 13:12:57 -0700 Subject: [PATCH] Move Sycamore and Sycamore23 to GridDevice. (#5544) Blocking SerializableDevice deprecation (#5522) Recirq needs to be updated to use `Sycamore.metadata.qubit_set` in a follow-up. I put deprecation warnings as docstrings/comments since I don't expect them to have much usage outside Cirq (one exception being `_SYCAMORE_DURATION_PICOS` is used on server side to serialize DeviceSpecification the old way). Let me know if you'd prefer a full deprecation warning. @dstrain115 cc @MichaelBroughton @mpharrigan --- .../cirq_google/devices/known_devices.py | 98 ++++- .../cirq_google/devices/known_devices_test.py | 345 +++++++++++------- .../devices/serializable_device_test.py | 221 ++++++----- .../engine/engine_processor_test.py | 38 +- docs/tutorials/educators/intro.ipynb | 5 +- examples/advanced/quantum_volume.py | 2 +- examples/advanced/quantum_volume_errors.ipynb | 12 +- examples/heatmaps.py | 19 +- 8 files changed, 447 insertions(+), 293 deletions(-) diff --git a/cirq-google/cirq_google/devices/known_devices.py b/cirq-google/cirq_google/devices/known_devices.py index fa19292dbcc..0532486ada3 100644 --- a/cirq-google/cirq_google/devices/known_devices.py +++ b/cirq-google/cirq_google/devices/known_devices.py @@ -12,13 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Collection, Dict, Optional, Iterable, List, Set, Tuple +from typing import Collection, Dict, Optional, Iterable, List, Set, Tuple, cast import cirq +from cirq import _compat from cirq_google.api import v2 from cirq_google.api.v2 import device_pb2 -from cirq_google.devices.serializable_device import SerializableDevice -from cirq_google.serialization import gate_sets, op_serializer, serializable_gate_set +from cirq_google.devices import grid_device +from cirq_google.experimental.ops import coupler_pulse +from cirq_google.ops import physical_z_tag, sycamore_gate +from cirq_google.serialization import op_serializer, serializable_gate_set _2_QUBIT_TARGET_SET = "2_qubit_targets" _MEAS_TARGET_SET = "meas_targets" @@ -52,6 +55,13 @@ def _parse_device(s: str) -> Tuple[List[cirq.GridQubit], Dict[str, Set[cirq.Grid return qubits, measurement_lines +@_compat.deprecated( + deadline='v0.16', + fix='This function will no longer be available.' + ' `cirq_google.grid_device.create_device_specification_proto()` can be used' + ' to generate a DeviceSpecification proto which matches the format expected' + ' by GridDevice.', +) def create_device_proto_from_diagram( ascii_grid: str, gate_sets: Optional[Iterable[serializable_gate_set.SerializableGateSet]] = None, @@ -59,10 +69,8 @@ def create_device_proto_from_diagram( out: Optional[device_pb2.DeviceSpecification] = None, ) -> device_pb2.DeviceSpecification: """Parse ASCIIart device layout into DeviceSpecification proto. - This function assumes that all pairs of adjacent qubits are valid targets for two-qubit gates. - Args: ascii_grid: ASCII version of the grid (see _parse_device for details). gate_sets: Gate sets that define the translation between gate ids and @@ -83,6 +91,39 @@ def create_device_proto_from_diagram( return create_device_proto_for_qubits(qubits, pairs, gate_sets, durations_picos, out) +def _create_grid_device_from_diagram( + ascii_grid: str, + gateset: cirq.Gateset, + gate_durations: Optional[Dict['cirq.GateFamily', 'cirq.Duration']] = None, + out: Optional[device_pb2.DeviceSpecification] = None, +) -> grid_device.GridDevice: + """Parse ASCIIart device layout into a GridDevice instance. + + This function assumes that all pairs of adjacent qubits are valid targets + for two-qubit gates. + + Args: + ascii_grid: ASCII version of the grid (see _parse_device for details). + gateset: The device's gate set. + gate_durations: A map of durations for each gate in the gate set. + out: If given, populate this proto, otherwise create a new proto. + """ + qubits, _ = _parse_device(ascii_grid) + + # Create a list of all adjacent pairs on the grid for two-qubit gates. + qubit_set = frozenset(qubits) + pairs: List[Tuple[cirq.GridQubit, cirq.GridQubit]] = [] + for qubit in qubits: + for neighbor in sorted(qubit.neighbors()): + if neighbor > qubit and neighbor in qubit_set: + pairs.append((qubit, cast(cirq.GridQubit, neighbor))) + + device_specification = grid_device.create_device_specification_proto( + qubits=qubits, pairs=pairs, gateset=gateset, gate_durations=gate_durations, out=out + ) + return grid_device.GridDevice.from_proto(device_specification) + + def create_device_proto_for_qubits( qubits: Collection[cirq.Qid], pairs: Collection[Tuple[cirq.Qid, cirq.Qid]], @@ -220,6 +261,8 @@ def populate_qubit_pairs_in_device_proto( ----I----- """ + +# Deprecated: replaced by _SYCAMORE_DURATIONS _SYCAMORE_DURATIONS_PICOS = { 'xy': 25_000, 'xy_half_pi': 25_000, @@ -232,14 +275,40 @@ def populate_qubit_pairs_in_device_proto( 'meas': 4_000_000, # 1000 ns for readout, 3000ns for ring_down } -SYCAMORE_PROTO = create_device_proto_from_diagram( - _SYCAMORE_GRID, [gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET], _SYCAMORE_DURATIONS_PICOS -) -Sycamore = SerializableDevice.from_proto( - proto=SYCAMORE_PROTO, gate_sets=[gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET] +_SYCAMORE_GATESET = cirq.Gateset( + sycamore_gate.SYC, + cirq.SQRT_ISWAP, + cirq.SQRT_ISWAP_INV, + cirq.PhasedXZGate, + # Physical Z and virtual Z gates are represented separately because they + # have different gate durations. + cirq.GateFamily(cirq.ZPowGate, tags_to_ignore=[physical_z_tag.PhysicalZTag()]), + cirq.GateFamily(cirq.ZPowGate, tags_to_accept=[physical_z_tag.PhysicalZTag()]), + coupler_pulse.CouplerPulse, + cirq.MeasurementGate, + cirq.WaitGate, ) + +_SYCAMORE_DURATIONS = { + cirq.GateFamily(sycamore_gate.SYC): cirq.Duration(nanos=12), + cirq.GateFamily(cirq.SQRT_ISWAP): cirq.Duration(nanos=32), + cirq.GateFamily(cirq.SQRT_ISWAP_INV): cirq.Duration(nanos=32), + cirq.GateFamily(cirq.ops.phased_x_z_gate.PhasedXZGate): cirq.Duration(nanos=25), + cirq.GateFamily( + cirq.ops.common_gates.ZPowGate, tags_to_ignore=[physical_z_tag.PhysicalZTag()] + ): cirq.Duration(nanos=0), + cirq.GateFamily( + cirq.ops.common_gates.ZPowGate, tags_to_accept=[physical_z_tag.PhysicalZTag()] + ): cirq.Duration(nanos=20), + cirq.GateFamily(cirq.ops.measurement_gate.MeasurementGate): cirq.Duration(millis=4), +} + + +Sycamore = _create_grid_device_from_diagram(_SYCAMORE_GRID, _SYCAMORE_GATESET, _SYCAMORE_DURATIONS) + + # Subset of the Sycamore grid with a reduced layout. _SYCAMORE23_GRID = """ ---------- @@ -254,12 +323,7 @@ def populate_qubit_pairs_in_device_proto( ----I----- """ -SYCAMORE23_PROTO = create_device_proto_from_diagram( - _SYCAMORE23_GRID, - [gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET], - _SYCAMORE_DURATIONS_PICOS, -) -Sycamore23 = SerializableDevice.from_proto( - proto=SYCAMORE23_PROTO, gate_sets=[gate_sets.SQRT_ISWAP_GATESET, gate_sets.SYC_GATESET] +Sycamore23 = _create_grid_device_from_diagram( + _SYCAMORE23_GRID, _SYCAMORE_GATESET, _SYCAMORE_DURATIONS ) diff --git a/cirq-google/cirq_google/devices/known_devices_test.py b/cirq-google/cirq_google/devices/known_devices_test.py index 6fb719ab0c3..a62c77a2b0a 100644 --- a/cirq-google/cirq_google/devices/known_devices_test.py +++ b/cirq-google/cirq_google/devices/known_devices_test.py @@ -16,6 +16,7 @@ import cirq import cirq_google +import cirq_google.experimental.ops.coupler_pulse as coupler_pulse import cirq_google.devices.known_devices as known_devices import cirq_google.serialization.common_serializers as cgc @@ -60,24 +61,25 @@ def test_create_device_proto_for_irregular_grid(): def test_multiple_gate_sets(): - halfPiGateSet = cirq_google.SerializableGateSet( - gate_set_name='half_pi_gateset', - serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS, cgc.MEASUREMENT_SERIALIZER], - deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS, cgc.MEASUREMENT_DESERIALIZER], - ) - durations_dict = { - 'xy_pi': 20_000, - 'xy_half_pi': 10_000, - 'xy': 53_000, - 'cz': 11_000, - 'meas': 14_141, - } - test_proto = known_devices.create_device_proto_from_diagram( - "aa\naa", [cirq_google.XMON, halfPiGateSet], durations_dict - ) - assert ( - str(test_proto) - == """\ + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + halfPiGateSet = cirq_google.SerializableGateSet( + gate_set_name='half_pi_gateset', + serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS, cgc.MEASUREMENT_SERIALIZER], + deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS, cgc.MEASUREMENT_DESERIALIZER], + ) + durations_dict = { + 'xy_pi': 20_000, + 'xy_half_pi': 10_000, + 'xy': 53_000, + 'cz': 11_000, + 'meas': 14_141, + } + test_proto = known_devices.create_device_proto_from_diagram( + "aa\naa", [cirq_google.XMON, halfPiGateSet], durations_dict + ) + assert ( + str(test_proto) + == """\ valid_gate_sets { name: "xmon" valid_gates { @@ -215,63 +217,35 @@ def test_multiple_gate_sets(): } } """ - ) - - -@pytest.mark.parametrize('device', [cirq_google.Sycamore, cirq_google.Sycamore23]) -def test_sycamore_devices(device): - 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) - device.validate_operation(syc) - device.validate_operation(sqrt_iswap) - assert device.duration_of(syc) == cirq.Duration(nanos=12) - assert device.duration_of(sqrt_iswap) == cirq.Duration(nanos=32) - - -def test_sycamore_metadata(): - assert len(cirq_google.Sycamore.metadata.qubit_pairs) == 88 - assert len(cirq_google.Sycamore23.metadata.qubit_pairs) == 32 - assert cirq_google.Sycamore.metadata.gateset == cirq.Gateset( - cirq.FSimGate, - cirq.ISwapPowGate, - cirq.PhasedXPowGate, - cirq.XPowGate, - cirq.YPowGate, - cirq.ZPowGate, - cirq.PhasedXZGate, - cirq.MeasurementGate, - cirq.WaitGate, - cirq.GlobalPhaseGate, - ) + ) def test_sycamore_circuitop_device(): - circuitop_gateset = cirq_google.SerializableGateSet( - gate_set_name='circuitop_gateset', - serializers=[cgc.CIRCUIT_OP_SERIALIZER], - deserializers=[cgc.CIRCUIT_OP_DESERIALIZER], - ) - gateset_list = [cirq_google.SQRT_ISWAP_GATESET, cirq_google.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) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + circuitop_gateset = cirq_google.SerializableGateSet( + gate_set_name='circuitop_gateset', + serializers=[cgc.CIRCUIT_OP_SERIALIZER], + deserializers=[cgc.CIRCUIT_OP_DESERIALIZER], + ) + gateset_list = [cirq_google.SQRT_ISWAP_GATESET, cirq_google.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(): @@ -290,18 +264,19 @@ def test_sycamore_grid_layout(): def test_proto_with_circuitop(): - circuitop_gateset = cirq_google.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] - ) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + circuitop_gateset = cirq_google.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) - == """\ + assert ( + str(circuitop_proto) + == """\ valid_gate_sets { name: "circuitop_gateset" valid_gates { @@ -337,28 +312,29 @@ def test_proto_with_circuitop(): } } """ - ) + ) def test_proto_with_waitgate(): - wait_gateset = cirq_google.SerializableGateSet( - gate_set_name='wait_gateset', - serializers=[cgc.WAIT_GATE_SERIALIZER], - deserializers=[cgc.WAIT_GATE_DESERIALIZER], - ) - wait_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa\naa", [wait_gateset] - ) - wait_device = cirq_google.SerializableDevice.from_proto( - proto=wait_proto, gate_sets=[wait_gateset] - ) - q0 = cirq.GridQubit(1, 1) - wait_op = cirq.wait(q0, nanos=25) - wait_device.validate_operation(wait_op) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + wait_gateset = cirq_google.SerializableGateSet( + gate_set_name='wait_gateset', + serializers=[cgc.WAIT_GATE_SERIALIZER], + deserializers=[cgc.WAIT_GATE_DESERIALIZER], + ) + wait_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa\naa", [wait_gateset] + ) + wait_device = cirq_google.SerializableDevice.from_proto( + proto=wait_proto, gate_sets=[wait_gateset] + ) + q0 = cirq.GridQubit(1, 1) + wait_op = cirq.wait(q0, nanos=25) + wait_device.validate_operation(wait_op) - assert ( - str(wait_proto) - == """\ + assert ( + str(wait_proto) + == """\ valid_gate_sets { name: "wait_gateset" valid_gates { @@ -399,32 +375,37 @@ def test_proto_with_waitgate(): } } """ - ) + ) def test_adding_gates_multiple_times(): - waiting_for_godot = cirq_google.SerializableGateSet( - gate_set_name='wait_gateset', - serializers=[cgc.WAIT_GATE_SERIALIZER, cgc.WAIT_GATE_SERIALIZER, cgc.WAIT_GATE_SERIALIZER], - deserializers=[ - cgc.WAIT_GATE_DESERIALIZER, - cgc.WAIT_GATE_DESERIALIZER, - cgc.WAIT_GATE_DESERIALIZER, - ], - ) - wait_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa", [waiting_for_godot] - ) - wait_device = cirq_google.SerializableDevice.from_proto( - proto=wait_proto, gate_sets=[waiting_for_godot] - ) - q0 = cirq.GridQubit(0, 0) - wait_op = cirq.wait(q0, nanos=25) - wait_device.validate_operation(wait_op) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + waiting_for_godot = cirq_google.SerializableGateSet( + gate_set_name='wait_gateset', + serializers=[ + cgc.WAIT_GATE_SERIALIZER, + cgc.WAIT_GATE_SERIALIZER, + cgc.WAIT_GATE_SERIALIZER, + ], + deserializers=[ + cgc.WAIT_GATE_DESERIALIZER, + cgc.WAIT_GATE_DESERIALIZER, + cgc.WAIT_GATE_DESERIALIZER, + ], + ) + wait_proto = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa", [waiting_for_godot] + ) + wait_device = cirq_google.SerializableDevice.from_proto( + proto=wait_proto, gate_sets=[waiting_for_godot] + ) + q0 = cirq.GridQubit(0, 0) + wait_op = cirq.wait(q0, nanos=25) + wait_device.validate_operation(wait_op) - assert ( - str(wait_proto) - == """\ + assert ( + str(wait_proto) + == """\ valid_gate_sets { name: "wait_gateset" valid_gates { @@ -451,4 +432,122 @@ def test_adding_gates_multiple_times(): } } """ + ) + + +@pytest.mark.parametrize( + 'device, qubit_size, layout_str', + [ + ( + cirq_google.Sycamore, + 88, + """\ + (0, 5)───(0, 6) + │ │ + │ │ + (1, 4)───(1, 5)───(1, 6)───(1, 7) + │ │ │ │ + │ │ │ │ + (2, 3)───(2, 4)───(2, 5)───(2, 6)───(2, 7)───(2, 8) + │ │ │ │ │ │ + │ │ │ │ │ │ + (3, 2)───(3, 3)───(3, 4)───(3, 5)───(3, 6)───(3, 7)───(3, 8)───(3, 9) + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ + (4, 1)───(4, 2)───(4, 3)───(4, 4)───(4, 5)───(4, 6)───(4, 7)───(4, 8)───(4, 9) + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ +(5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4)───(5, 5)───(5, 6)───(5, 7)───(5, 8) + │ │ │ │ │ │ │ + │ │ │ │ │ │ │ + (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5)───(6, 6)───(6, 7) + │ │ │ │ │ + │ │ │ │ │ + (7, 2)───(7, 3)───(7, 4)───(7, 5)───(7, 6) + │ │ │ + │ │ │ + (8, 3)───(8, 4)───(8, 5) + │ + │ + (9, 4)""", + ), + ( + cirq_google.Sycamore23, + 32, + """\ + (3, 2) + │ + │ + (4, 1)───(4, 2)───(4, 3) + │ │ │ + │ │ │ +(5, 0)───(5, 1)───(5, 2)───(5, 3)───(5, 4) + │ │ │ │ + │ │ │ │ + (6, 1)───(6, 2)───(6, 3)───(6, 4)───(6, 5) + │ │ │ │ + │ │ │ │ + (7, 2)───(7, 3)───(7, 4)───(7, 5)───(7, 6) + │ │ │ + │ │ │ + (8, 3)───(8, 4)───(8, 5) + │ + │ + (9, 4)""", + ), + ], +) +def test_sycamore_devices(device, qubit_size, layout_str): + q0 = cirq.GridQubit(5, 3) + q1 = cirq.GridQubit(5, 4) + valid_sycamore_gates_and_ops = [ + cirq_google.SYC, + cirq.SQRT_ISWAP, + cirq.SQRT_ISWAP_INV, + cirq.X, + cirq.Y, + # Broken due to issue #5543. + # TODO(#5543) Uncomment + # cirq.Z, + # cirq.Z(q0).with_tags(cirq_google.PhysicalZTag()), + coupler_pulse.CouplerPulse(hold_time=cirq.Duration(nanos=10), coupling_mhz=25.0), + cirq.measure(q0), + cirq.WaitGate(cirq.Duration(millis=5)), + # TODO(#5050) Uncomment after GlobalPhaseGate support is added. + # cirq.GlobalPhaseGate(-1.0), + ] + 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) + + assert str(device) == layout_str + assert len(device.metadata.qubit_pairs) == qubit_size + assert all(gate_or_op in device.metadata.gateset for gate_or_op in valid_sycamore_gates_and_ops) + assert len(device.metadata.gate_durations) == len(device.metadata.gateset.gates) + assert any( + isinstance(cgs, cirq_google.SycamoreTargetGateset) + for cgs in device.metadata.compilation_target_gatesets + ) + assert any( + isinstance(cgs, cirq.SqrtIswapTargetGateset) + for cgs in device.metadata.compilation_target_gatesets ) + + device.validate_operation(syc) + device.validate_operation(sqrt_iswap) + + assert next( + ( + duration + for gate_family, duration in device.metadata.gate_durations.items() + if syc in gate_family + ), + None, + ) == cirq.Duration(nanos=12) + assert next( + ( + duration + for gate_family, duration in device.metadata.gate_durations.items() + if sqrt_iswap in gate_family + ), + None, + ) == cirq.Duration(nanos=32) diff --git a/cirq-google/cirq_google/devices/serializable_device_test.py b/cirq-google/cirq_google/devices/serializable_device_test.py index 1ed37823877..8b93c850724 100644 --- a/cirq-google/cirq_google/devices/serializable_device_test.py +++ b/cirq-google/cirq_google/devices/serializable_device_test.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy import textwrap import unittest.mock as mock @@ -59,7 +58,7 @@ def test_str_with_grid_qubits(): ], gate_sets=[cg.FSIM_GATESET], ) - device = cgdk.SerializableDevice.from_proto(device_proto, gate_sets=[cg.FSIM_GATESET]) + device = cg.SerializableDevice.from_proto(device_proto, gate_sets=[cg.FSIM_GATESET]) assert str(device) == textwrap.dedent( """\ q(1, 1)───q(1, 2) q(1, 3) @@ -88,7 +87,7 @@ def test_metadata_correct(): device_proto = cgdk.create_device_proto_for_qubits( qubits=qubits, pairs=pairs, gate_sets=[cg.FSIM_GATESET] ) - device = cgdk.SerializableDevice.from_proto(device_proto, gate_sets=[cg.FSIM_GATESET]) + device = cg.SerializableDevice.from_proto(device_proto, gate_sets=[cg.FSIM_GATESET]) assert device.metadata.qubit_pairs == frozenset({frozenset(p) for p in pairs}) assert device.metadata.gateset == cirq.Gateset( cirq.FSimGate, @@ -133,37 +132,53 @@ def test_gate_definition_equality(): def test_mismatched_proto_serializer(): - augmented_proto = copy.deepcopy(cg.devices.known_devices.SYCAMORE_PROTO) - # Remove measurement gate - del augmented_proto.valid_gate_sets[0].valid_gates[3] + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + augmented_proto = cgdk.create_device_proto_from_diagram( + cgdk._SYCAMORE_GRID, + [cg.SQRT_ISWAP_GATESET, cg.SYC_GATESET], + cgdk._SYCAMORE_DURATIONS_PICOS, + ) + # Remove measurement gate + del augmented_proto.valid_gate_sets[0].valid_gates[3] - # Should throw value error that measurement gate is serialized - # but not supported by the hardware - with pytest.raises(ValueError): - _ = cg.SerializableDevice.from_proto(proto=augmented_proto, gate_sets=[cg.XMON]) + # Should throw value error that measurement gate is serialized + # but not supported by the hardware + with pytest.raises(ValueError): + _ = cg.SerializableDevice.from_proto(proto=augmented_proto, gate_sets=[cg.XMON]) def test_named_qubit(): - augmented_proto = copy.deepcopy(cg.devices.known_devices.SYCAMORE_PROTO) - augmented_proto.valid_qubits.extend(["scooby_doo"]) - sycamore = cg.SerializableDevice.from_proto(proto=augmented_proto, gate_sets=[cg.SYC_GATESET]) - sycamore.validate_operation(cirq.X(cirq.NamedQubit("scooby_doo"))) - with pytest.raises(ValueError): - sycamore.validate_operation(cirq.X(cirq.NamedQubit("scrappy_doo"))) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + augmented_proto = cgdk.create_device_proto_from_diagram( + cgdk._SYCAMORE_GRID, + [cg.SQRT_ISWAP_GATESET, cg.SYC_GATESET], + cgdk._SYCAMORE_DURATIONS_PICOS, + ) + augmented_proto.valid_qubits.extend(["scooby_doo"]) + sycamore = cg.SerializableDevice.from_proto( + proto=augmented_proto, gate_sets=[cg.SYC_GATESET] + ) + sycamore.validate_operation(cirq.X(cirq.NamedQubit("scooby_doo"))) + with pytest.raises(ValueError): + sycamore.validate_operation(cirq.X(cirq.NamedQubit("scrappy_doo"))) def test_duration_of(): - valid_qubit1 = cirq.GridQubit(0, 0) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + valid_qubit1 = cirq.GridQubit(0, 0) - sycamore = cg.SerializableDevice.from_proto( - proto=cg.devices.known_devices.SYCAMORE_PROTO, gate_sets=[cg.SYC_GATESET] - ) + sycamore_proto = cgdk.create_device_proto_from_diagram( + cgdk._SYCAMORE_GRID, [cg.SYC_GATESET], cgdk._SYCAMORE_DURATIONS_PICOS + ) + sycamore = cg.SerializableDevice.from_proto( + proto=sycamore_proto, gate_sets=[cg.SYC_GATESET] + ) - assert sycamore.duration_of(cirq.X(valid_qubit1)) == cirq.Duration(nanos=25) + assert sycamore.duration_of(cirq.X(valid_qubit1)) == cirq.Duration(nanos=25) - # Unsupported op - with pytest.raises(ValueError): - assert sycamore.duration_of(cirq.H(valid_qubit1)) + # Unsupported op + with pytest.raises(ValueError): + assert sycamore.duration_of(cirq.H(valid_qubit1)) def test_asymmetric_gate(): @@ -302,30 +317,33 @@ def test_mixing_types(): def test_multiple_gatesets(): - halfPiGateSet = cirq_google.SerializableGateSet( - gate_set_name='half_pi_gateset', - serializers=cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS, - deserializers=cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS, - ) - allAnglesGateSet = cirq_google.SerializableGateSet( - gate_set_name='all_angles_gateset', - serializers=cgc.SINGLE_QUBIT_SERIALIZERS, - deserializers=cgc.SINGLE_QUBIT_DESERIALIZERS, - ) - durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 20_000, 'xy': 20_000} - spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa\naa", [allAnglesGateSet, halfPiGateSet], durations_dict - ) - dev = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[allAnglesGateSet, halfPiGateSet]) - q0 = cirq.GridQubit(0, 0) - q1 = cirq.GridQubit(1, 0) - dev.validate_operation(cirq.X(q0)) - dev.validate_operation(cirq.X(q1)) - dev.validate_operation(cirq.XPowGate(exponent=0.1234)(q0)) - dev.validate_operation(cirq.XPowGate(exponent=0.2345)(q1)) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + halfPiGateSet = cirq_google.SerializableGateSet( + gate_set_name='half_pi_gateset', + serializers=cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS, + deserializers=cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS, + ) + allAnglesGateSet = cirq_google.SerializableGateSet( + gate_set_name='all_angles_gateset', + serializers=cgc.SINGLE_QUBIT_SERIALIZERS, + deserializers=cgc.SINGLE_QUBIT_DESERIALIZERS, + ) + durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 20_000, 'xy': 20_000} + spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa\naa", [allAnglesGateSet, halfPiGateSet], durations_dict + ) + dev = cg.SerializableDevice.from_proto( + proto=spec, gate_sets=[allAnglesGateSet, halfPiGateSet] + ) + q0 = cirq.GridQubit(0, 0) + q1 = cirq.GridQubit(1, 0) + dev.validate_operation(cirq.X(q0)) + dev.validate_operation(cirq.X(q1)) + dev.validate_operation(cirq.XPowGate(exponent=0.1234)(q0)) + dev.validate_operation(cirq.XPowGate(exponent=0.2345)(q1)) - with pytest.raises(ValueError): - dev.validate_operation(cirq.X(cirq.GridQubit(2, 2))) + with pytest.raises(ValueError): + dev.validate_operation(cirq.X(cirq.GridQubit(2, 2))) def test_half_pi_takes_half_duration(): @@ -333,23 +351,24 @@ def test_half_pi_takes_half_duration(): gate perform correctly. In this case, we set the XPowGate to be half the duration of the full exponent and make sure it still works. """ - half_pi_gs = cirq_google.SerializableGateSet( - gate_set_name='half_pi', - serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS], - deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS], - ) - durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 10_000} - spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa\naa", [half_pi_gs], durations_dict - ) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + half_pi_gs = cirq_google.SerializableGateSet( + gate_set_name='half_pi', + serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS], + deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS], + ) + durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 10_000} + spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa\naa", [half_pi_gs], durations_dict + ) - # The gate set defines two different serializations for PhasedXPowGate - device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[half_pi_gs]) - q = cirq.GridQubit(0, 0) - pi = cirq.XPowGate(exponent=1.0) - half_pi = cirq.XPowGate(exponent=0.5) - assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000) - assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000) + # The gate set defines two different serializations for PhasedXPowGate + device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[half_pi_gs]) + q = cirq.GridQubit(0, 0) + pi = cirq.XPowGate(exponent=1.0) + half_pi = cirq.XPowGate(exponent=0.5) + assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000) + assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000) def test_multiple_fsim_gatesets(): @@ -357,38 +376,40 @@ def test_multiple_fsim_gatesets(): gate perform correctly. In this case, we set the XPowGate to be half the duration of the full exponent and make sure it still works. """ - half_pi_gs = cirq_google.SerializableGateSet( - gate_set_name='half_pi', - serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS], - deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS], - ) - durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 10_000} - spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa\naa", [half_pi_gs], durations_dict - ) + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + half_pi_gs = cirq_google.SerializableGateSet( + gate_set_name='half_pi', + serializers=[*cgc.SINGLE_QUBIT_HALF_PI_SERIALIZERS], + deserializers=[*cgc.SINGLE_QUBIT_HALF_PI_DESERIALIZERS], + ) + durations_dict = {'xy_pi': 20_000, 'xy_half_pi': 10_000} + spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa\naa", [half_pi_gs], durations_dict + ) - # The gate set defines two different serializations for PhasedXPowGate - device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[half_pi_gs]) - q = cirq.GridQubit(0, 0) - pi = cirq.XPowGate(exponent=1.0) - half_pi = cirq.XPowGate(exponent=0.5) - assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000) - assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000) + # The gate set defines two different serializations for PhasedXPowGate + device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[half_pi_gs]) + q = cirq.GridQubit(0, 0) + pi = cirq.XPowGate(exponent=1.0) + half_pi = cirq.XPowGate(exponent=0.5) + assert device.duration_of(pi(q)) == cirq.Duration(picos=20_000) + assert device.duration_of(half_pi(q)) == cirq.Duration(picos=10_000) def test_serializable_device_str_grid_qubits(): - spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( - "aa\naa", [cg.SYC_GATESET] - ) - device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[cg.SYC_GATESET]) - assert ( - str(device) - == """\ + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + spec = cirq_google.devices.known_devices.create_device_proto_from_diagram( + "aa\naa", [cg.SYC_GATESET] + ) + device = cg.SerializableDevice.from_proto(proto=spec, gate_sets=[cg.SYC_GATESET]) + assert ( + str(device) + == """\ q(0, 0)───q(0, 1) │ │ │ │ q(1, 0)───q(1, 1)""" - ) + ) def test_serializable_device_str_named_qubits(): @@ -406,29 +427,3 @@ def test_serializable_device_gate_definitions_filter(): ) # Two gates for cirq.FSimGate and the cirq.GlobalPhaseGate default assert len(device.metadata.gateset.gates) == 2 - - -def test_sycamore23_str(): - assert ( - str(cg.Sycamore23) - == """\ - q(3, 2) - │ - │ - q(4, 1)───q(4, 2)───q(4, 3) - │ │ │ - │ │ │ -q(5, 0)───q(5, 1)───q(5, 2)───q(5, 3)───q(5, 4) - │ │ │ │ - │ │ │ │ - q(6, 1)───q(6, 2)───q(6, 3)───q(6, 4)───q(6, 5) - │ │ │ │ - │ │ │ │ - q(7, 2)───q(7, 3)───q(7, 4)───q(7, 5)───q(7, 6) - │ │ │ - │ │ │ - q(8, 3)───q(8, 4)───q(8, 5) - │ - │ - q(9, 4)""" - ) diff --git a/cirq-google/cirq_google/engine/engine_processor_test.py b/cirq-google/cirq_google/engine/engine_processor_test.py index bd832db0bf3..809873f690a 100644 --- a/cirq-google/cirq_google/engine/engine_processor_test.py +++ b/cirq-google/cirq_google/engine/engine_processor_test.py @@ -330,23 +330,27 @@ def test_get_device(): def test_default_gate_sets(): - # Sycamore should have valid gate sets with default - processor = cg.EngineProcessor( - 'a', - 'p', - EngineContext(), - _processor=quantum.QuantumProcessor( - device_spec=util.pack_any(known_devices.SYCAMORE_PROTO) - ), - ) - device = processor.get_device() - device.validate_operation(cirq.X(cirq.GridQubit(5, 4))) - # Test that a device with no standard gatesets doesn't blow up - processor = cg.EngineProcessor( - 'a', 'p', EngineContext(), _processor=quantum.QuantumProcessor(device_spec=_DEVICE_SPEC) - ) - device = processor.get_device() - assert device.qubits == [cirq.GridQubit(0, 0), cirq.GridQubit(1, 1)] + with cirq.testing.assert_deprecated('no longer be available', deadline='v0.16', count=1): + # Sycamore should have valid gate sets with default + sycamore_proto = known_devices.create_device_proto_from_diagram( + known_devices._SYCAMORE_GRID, + [cg.SQRT_ISWAP_GATESET, cg.SYC_GATESET], + known_devices._SYCAMORE_DURATIONS_PICOS, + ) + processor = cg.EngineProcessor( + 'a', + 'p', + EngineContext(), + _processor=quantum.QuantumProcessor(device_spec=util.pack_any(sycamore_proto)), + ) + device = processor.get_device() + device.validate_operation(cirq.X(cirq.GridQubit(5, 4))) + # Test that a device with no standard gatesets doesn't blow up + processor = cg.EngineProcessor( + 'a', 'p', EngineContext(), _processor=quantum.QuantumProcessor(device_spec=_DEVICE_SPEC) + ) + device = processor.get_device() + assert device.qubits == [cirq.GridQubit(0, 0), cirq.GridQubit(1, 1)] def test_get_missing_device(): diff --git a/docs/tutorials/educators/intro.ipynb b/docs/tutorials/educators/intro.ipynb index ed319741466..7a770167d7a 100644 --- a/docs/tutorials/educators/intro.ipynb +++ b/docs/tutorials/educators/intro.ipynb @@ -2490,7 +2490,10 @@ "source": [ "\"\"\"Get the duration of an operation.\"\"\"\n", "op = cirq.X.on(cirq.GridQubit(5, 5))\n", - "print(cirq_google.Sycamore.duration_of(op))" + "gate_durations = cirq_google.Sycamore.metadata.gate_durations\n", + "for gate_family in gate_durations:\n", + " if op in gate_family:\n", + " print(gate_durations[gate_family])" ] }, { diff --git a/examples/advanced/quantum_volume.py b/examples/advanced/quantum_volume.py index f9c8c40f566..e6b7577a3e4 100644 --- a/examples/advanced/quantum_volume.py +++ b/examples/advanced/quantum_volume.py @@ -49,7 +49,7 @@ def main(*, num_qubits: int, depth: int, num_circuits: int, seed: int, routes: i depth=depth, num_circuits=num_circuits, random_state=seed, - device_graph=routing.gridqubits_to_graph_device(device.qubits), + device_graph=routing.gridqubits_to_graph_device(device.metadata.qubit_set), samplers=[cirq.Simulator(), noisy], routing_attempts=routes, compiler=compiler, diff --git a/examples/advanced/quantum_volume_errors.ipynb b/examples/advanced/quantum_volume_errors.ipynb index c71117bde31..fcc5783b091 100644 --- a/examples/advanced/quantum_volume_errors.ipynb +++ b/examples/advanced/quantum_volume_errors.ipynb @@ -71,7 +71,7 @@ " num_circuits=num_circuits,\n", " depth=depth,\n", " num_qubits=depth,\n", - " device_graph=routing.gridqubits_to_graph_device(device.qubits),\n", + " device_graph=routing.gridqubits_to_graph_device(device.metadata.qubit_set),\n", " samplers=samplers,\n", " compiler=cirq_google.optimized_for_sycamore,\n", " repetitions=repetitions)" @@ -116,6 +116,10 @@ } ], "metadata": { + "colab": { + "name": "quantum_volume_errors.ipynb", + "provenance": [] + }, "kernelspec": { "display_name": "Python 3", "language": "python", @@ -132,12 +136,8 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.9" - }, - "colab": { - "name": "quantum_volume_errors.ipynb", - "provenance": [] } }, "nbformat": 4, "nbformat_minor": 0 -} \ No newline at end of file +} diff --git a/examples/heatmaps.py b/examples/heatmaps.py index e0bb89ec565..772eb3174a5 100644 --- a/examples/heatmaps.py +++ b/examples/heatmaps.py @@ -11,26 +11,13 @@ def single_qubit_heatmap(): """Demo of cirq.Heatmap. Demonstrates how cirq.Heatmap can be used to generate a heatmap of the qubit fidelities. """ - value_map = {(qubit,): np.random.random() for qubit in cirq_google.Sycamore.qubits} + value_map = {(qubit,): np.random.random() for qubit in cirq_google.Sycamore.metadata.qubit_set} heatmap = cirq.Heatmap(value_map) # This is going to produce an image similar to examples/single_qubit_heatmap_example.png heatmap.plot() -def _sycamore_qubit_pairs(): - # TODO(https://github.com/quantumlib/Cirq/issues/3696): replace this when we have a proper - # method for it - return set( - tuple(sorted(pair)) - for gate_defs in cirq_google.Sycamore.gate_definitions.values() - for gate_def in gate_defs - if gate_def.number_of_qubits == 2 - for pair in gate_def.target_set - if len(pair) == 2 - ) - - def two_qubit_interaction_heatmap(): """Demo of cirq.InteractionHeatmap. Demonstrates how cirq.Heatmap can be used to generate a two qubit interaction heatmap. @@ -38,7 +25,9 @@ def two_qubit_interaction_heatmap(): # normally one would get these from cirq_google.engine s = np.random.RandomState(1234) - random_characterization_data = {qubit_pair: s.rand() for qubit_pair in _sycamore_qubit_pairs()} + random_characterization_data = { + tuple(qubit_pair): s.rand() for qubit_pair in cirq_google.Sycamore.metadata.qubit_pairs + } heatmap = cirq.TwoQubitInteractionHeatmap( value_map=random_characterization_data,