From a759e41beb9ac481ce3e8032136f65089e9ce64e Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Mon, 4 May 2020 06:38:41 -0700 Subject: [PATCH] Add get_device for engine_processor (#2954) * Add get_device for engine_processor - Adds get_device() that returns a cirq.Device out of the device specification. - Also, cleaned up some documentation that was not using the engine processor correctly. --- cirq/google/engine/engine_processor.py | 20 ++++++++++- cirq/google/engine/engine_processor_test.py | 40 +++++++++++++++++++++ docs/google/devices.md | 17 ++++----- docs/google/specification.md | 23 ++++++------ 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/cirq/google/engine/engine_processor.py b/cirq/google/engine/engine_processor.py index a5aac058a44..d405443066f 100644 --- a/cirq/google/engine/engine_processor.py +++ b/cirq/google/engine/engine_processor.py @@ -13,17 +13,20 @@ # limitations under the License. import datetime -from typing import List, Optional, TYPE_CHECKING, Union, Tuple +from typing import Iterable, List, Optional, TYPE_CHECKING, Union from pytz import utc from cirq.google.engine.client.quantum import types as qtypes from cirq.google.engine.client.quantum import enums as qenums +from cirq.google import serializable_gate_set from cirq.google.api import v2 +from cirq.google.devices import serializable_device from cirq.google.engine import calibration from cirq.google.engine.engine_timeslot import EngineTimeSlot if TYPE_CHECKING: import cirq.google.engine.engine as engine_base + import cirq class EngineProcessor: @@ -108,6 +111,21 @@ def get_device_specification( else: return None + def get_device( + self, gate_sets: Iterable[serializable_gate_set.SerializableGateSet] + ) -> 'cirq.Device': + """Returns a `Device` created from the processor's device specification. + + This method queries the processor to retrieve the device specification, + which is then use to create a `SerializableDevice` that will validate + that operations are supported and use the correct qubits. + """ + spec = self.get_device_specification() + if not spec: + raise ValueError('Processor does not have a device specification') + return serializable_device.SerializableDevice.from_proto( + spec, gate_sets) + @staticmethod def _to_calibration(calibration_any: qtypes.any_pb2.Any ) -> calibration.Calibration: diff --git a/cirq/google/engine/engine_processor_test.py b/cirq/google/engine/engine_processor_test.py index 9d382e769a9..ba2c49f1634 100644 --- a/cirq/google/engine/engine_processor_test.py +++ b/cirq/google/engine/engine_processor_test.py @@ -20,6 +20,7 @@ from google.protobuf.duration_pb2 import Duration from google.protobuf.text_format import Merge from google.protobuf.timestamp_pb2 import Timestamp +import cirq import cirq.google as cg from cirq.google.api import v2 from cirq.google.engine.engine import EngineContext @@ -107,6 +108,21 @@ def _to_timestamp(json_string): """, v2.device_pb2.DeviceSpecification())) +_GATE_SET = cg.SerializableGateSet( + gate_set_name='x_gate_set', + serializers=[ + cg.GateOpSerializer(gate_type=cirq.XPowGate, + serialized_gate_id='x', + args=[]) + ], + deserializers=[ + cg.GateOpDeserializer(serialized_gate_id='x', + gate_constructor=cirq.XPowGate, + args=[]) + ], +) + + @pytest.fixture(scope='session', autouse=True) def mock_grpc_client(): with mock.patch('cirq.google.engine.engine_client' @@ -211,6 +227,30 @@ def test_get_device_specification(): assert processor.get_device_specification() == expected +def test_get_device(): + processor = cg.EngineProcessor( + 'a', + 'p', + EngineContext(), + _processor=qtypes.QuantumProcessor(device_spec=_DEVICE_SPEC)) + device = processor.get_device(gate_sets=[_GATE_SET]) + assert device.qubits == [cirq.GridQubit(0, 0), cirq.GridQubit(1, 1)] + device.validate_operation(cirq.X(cirq.GridQubit(0, 0))) + with pytest.raises(ValueError): + device.validate_operation(cirq.X(cirq.GridQubit(1, 2))) + with pytest.raises(ValueError): + device.validate_operation(cirq.Y(cirq.GridQubit(0, 0))) + + +def test_get_missing_device(): + processor = cg.EngineProcessor('a', + 'p', + EngineContext(), + _processor=qtypes.QuantumProcessor()) + with pytest.raises(ValueError, match='device specification'): + _ = processor.get_device(gate_sets=[_GATE_SET]) + + @mock.patch('cirq.google.engine.engine_client.EngineClient.list_calibrations') def test_list_calibrations(list_calibrations): list_calibrations.return_value = [_CALIBRATION] diff --git a/docs/google/devices.md b/docs/google/devices.md index 3bc51ae46b9..70342aa0cbe 100644 --- a/docs/google/devices.md +++ b/docs/google/devices.md @@ -5,19 +5,19 @@ Since quantum hardware is an active area of research, hardware specifications an practices are constantly evolving in an attempt to continuously improve performance. While this information should be a solid base for beginning your quantum application, please work with your Google sponsor to obtain the latest information on devices that -you plan to use. +you plan to use. ## General limitations Qubits on Google devices are laid out in a grid structure. Connectivity is limited to -adjacent qubits, either horizontally or vertically. +adjacent qubits, either horizontally or vertically. Measurement takes much longer than other gates. Currently, the only supported configuration is to have terminal measurement in the final moment of a circuit. Most devices have a limited set of gates that can be applied. Gates not in that set must be decomposed into an equivalent circuit using gates within the set. -See below for those restrictions. +See below for those restrictions. There are some limitations to the total circuit length due to hardware limitations. Several factors can influence this limit, but this can be estimated at about 40 microseconds @@ -30,12 +30,12 @@ of total circuit run-time. Circuits that exceed this limit will return a The Sycamore device is a 54 qubit device introduced in 2019 with a [publication in Nature](https://www.nature.com/articles/s41586-019-1666-5). -Note that the supremacy result in the paper utilized a device that had 53 qubits since +Note that the supremacy result in the paper utilized a device that had 53 qubits since one qubit had malfunctioned. It can be accessed using `cirq.GridQubit(row, col)` using grid coordinates specified below. -``` +``` 0123456789 0 -----AB--- 1 ----ABCD-- @@ -88,7 +88,7 @@ compilation as the Sycamore device. The Bristlecone processor is a 72 qubit device [announced by Google in 2018](https://ai.googleblog.com/2018/03/a-preview-of-bristlecone-googles-new.html). -The device is arrayed on a grid in a diamond pattern like this. +The device is arrayed on a grid in a diamond pattern like this. ``` 11 @@ -121,8 +121,3 @@ algorithms on NISQ devices. It can be accessing by using `cirq.google.Foxtail`. Circuits can be compiled to it by using `cirq.google.optimized_for_xmon` or by using `cirq.google.optimized_for_sycamore` with optimizer_type `xmon`. - - - - - diff --git a/docs/google/specification.md b/docs/google/specification.md index 3f3c54e7ddb..8359884b275 100644 --- a/docs/google/specification.md +++ b/docs/google/specification.md @@ -16,17 +16,17 @@ This specification is defined in the Device proto within `cirq.google.api.v2`. Most devices can only accept a limited set of gates. This is known as the gate set of the device. Any circuits sent to this device must only use gates -within this set. The gate set portion of the protocol buffer defines which +within this set. The gate set portion of the protocol buffer defines which gate set(s) are valid on the device, and which gates make up that set. ### Gate Definitions Each gate in the gate set will have a definition that defines the id that the gate is serialized as, the number of qubits for the gates, the arguments -to the gate, the duration, and which qubits it can be applied to. +to the gate, the duration, and which qubits it can be applied to. This definition uses "target sets" to specify which qubits the operation can -be applied to. See the section below for more information. +be applied to. See the section below for more information. ### Gate Durations @@ -34,7 +34,7 @@ The time it takes the device to perform each gate is stored within the device specification. This time is stored as an integer number of picoseconds. Example code to print out the gate durations for every gate supported by the -device is shown below: +device is shown below: ``` import cirq @@ -44,7 +44,7 @@ engine = cirq.google.Engine(project_id='your_project_id', proto_version=cirq.google.ProtoVersion.V2) # Replace the processor id to get the device specification with that id. -spec = engine.get_device_specification('processor_id') +spec = engine.get_processor('processor_id').get_device_specification() # Iterate through each gate set valid on the device. for gateset in spec.valid_gate_sets: @@ -82,7 +82,7 @@ the targets is valid. This is typically used for measurement gates. If `q0`, gate, then `gate.on(q0)`, `gate.on(q1)`, `gate.on(q2)`, `gate.on(q0, q1)`, `gate.on(q0, q2)`, `gate.on(q1, q2)` and `gate.on(q0, q1, q2)` are all valid uses of the gate. - + ### Developer Recommendations This is a free form text field for additional recommendations and soft @@ -113,12 +113,9 @@ import cirq.google as cg engine = cg.Engine(project_id='your_project_id', proto_version=cirq.google.ProtoVersion.V2) -# Replace the processor id to get the device specification with that id. -spec = engine.get_device_specification('processor_id') - -device = cg.SerializableDevice.from_proto( - proto=spec, - gate_sets=[cg.gate_sets.SQRT_ISWAP_GATESET) +# Replace the processor id to get the device with that id. +device = engine.get_processor('processor_id').get_device( + gate_sets=[cg.gate_sets.SQRT_ISWAP_GATESET]) q0, q1 = cirq.LineQubit.range(2) @@ -127,4 +124,4 @@ cirq.Circuit(cirq.CZ(q0,q1), device=device) ``` Note that, if network traffic is undesired, the `DeviceSpecification` can -easily be stored in either binary format or TextProto format for later usage. +easily be stored in either binary format or TextProto format for later usage.