14
14
15
15
"""Device object representing Google devices with a grid qubit layout."""
16
16
17
+ import re
18
+
17
19
from typing import Any , Set , Tuple , cast
18
20
import cirq
19
21
from cirq_google .api import v2
@@ -27,9 +29,16 @@ def _validate_device_specification(proto: v2.device_pb2.DeviceSpecification) ->
27
29
28
30
Raises:
29
31
ValueError: If the DeviceSpecification is invalid.
30
-
31
32
"""
32
33
34
+ # Qubit names must be in the form <int>_<int> to be parsed as cirq.GridQubits.
35
+ for q_name in proto .valid_qubits :
36
+ if re .match (r'^[0-9]+\_[0-9]+$' , q_name ) is None :
37
+ raise ValueError (
38
+ f"Invalid DeviceSpecification: valid_qubits contains the qubit '{ q_name } ' which is"
39
+ " not in the GridQubit form '<int>_<int>."
40
+ )
41
+
33
42
for target_set in proto .valid_targets :
34
43
35
44
# Check for unknown qubits in targets.
@@ -79,16 +88,16 @@ class GridDevice(cirq.Device):
79
88
Example use cases:
80
89
81
90
* Get an instance of a Google grid device.
82
- >>> device = cirq_google.get_engine().get_processor('weber ').get_device()
91
+ >>> device = cirq_google.get_engine().get_processor('processor_name ').get_device()
83
92
84
93
* Print the grid layout of the device.
85
94
>>> print(device)
86
95
87
96
* Determine whether a circuit can be run on the device.
88
- >>> device.validate_circuit(circuit) # Raises an exception if the circuit is invalid.
97
+ >>> device.validate_circuit(circuit) # Raises a ValueError if the circuit is invalid.
89
98
90
99
* Determine whether an operation can be run on the device.
91
- >>> device.validate_operation(operation) # Raises an exception if the operation is invalid.
100
+ >>> device.validate_operation(operation) # Raises a ValueError if the operation is invalid.
92
101
93
102
* Get the `cirq.Gateset` containing valid gates for the device, and inspect the full list
94
103
of valid gates.
@@ -116,7 +125,7 @@ class GridDevice(cirq.Device):
116
125
117
126
For Google devices, the
118
127
[DeviceSpecification proto](
119
- https://github.com/quantumlib/Cirq/blob/3969c2d3964cea56df33b329f036ba6810683882 /cirq-google/cirq_google/api/v2/device.proto#L13
128
+ https://github.com/quantumlib/Cirq/blob/master /cirq-google/cirq_google/api/v2/device.proto
120
129
)
121
130
is the main specification for device information surfaced by the Quantum Computing Service.
122
131
Thus, this class is should be instantiated using a `DeviceSpecification` proto via the
@@ -134,21 +143,29 @@ def __init__(self, metadata: cirq.GridDeviceMetadata):
134
143
def from_proto (cls , proto : v2 .device_pb2 .DeviceSpecification ) -> 'GridDevice' :
135
144
"""Create a `GridDevice` from a DeviceSpecification proto.
136
145
137
- This class only supports `cirq.GridQubit`s and `cirq.NamedQubit`s. If a
138
- `DeviceSpecification.valid_qubits` string is in the form `<int>_<int>`, it is parsed as a
139
- GridQubit. Otherwise it is parsed as a NamedQubit.
140
-
141
146
Args:
142
147
proto: The `DeviceSpecification` proto describing a Google device.
143
148
144
149
Raises:
145
- ValueError: If the given `DeviceSpecification` is invalid.
150
+ ValueError: If the given `DeviceSpecification` is invalid. It is invalid if:
151
+ * A `DeviceSpecification.valid_qubits` string is not in the form `<int>_<int>`, thus
152
+ cannot be parsed as a `cirq.GridQubit`.
153
+ * `DeviceSpecification.valid_targets` refer to qubits which are not in
154
+ `DeviceSpecification.valid_qubits`.
155
+ * A target set in `DeviceSpecification.valid_targets` has type `SYMMETRIC` or
156
+ `ASYMMETRIC` but contains targets with repeated qubits, e.g. a qubit pair with a
157
+ self loop.
158
+ * A target set in `DeviceSpecification.valid_targets` has type `SUBSET_PERMUTATION`
159
+ but contains targets which do not have exactly one element. A `SUBSET_PERMUTATION`
160
+ target set uses each target to represent a single qubit, and a gate can apply to
161
+ any subset of qubits in the target set.
162
+
146
163
"""
147
164
148
165
_validate_device_specification (proto )
149
166
150
167
# Create qubit set
151
- all_qubits = [_qid_from_str (q ) for q in proto .valid_qubits ]
168
+ all_qubits = [v2 . grid_qubit_from_proto_id (q ) for q in proto .valid_qubits ]
152
169
153
170
# Create qubit pair set
154
171
#
@@ -159,7 +176,7 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GridDevice':
159
176
# * Measurement gate can always be applied to all subset of qubits.
160
177
#
161
178
qubit_pairs = [
162
- (_qid_from_str (target .ids [0 ]), _qid_from_str (target .ids [1 ]))
179
+ (v2 . grid_qubit_from_proto_id (target .ids [0 ]), v2 . grid_qubit_from_proto_id (target .ids [1 ]))
163
180
for ts in proto .valid_targets
164
181
for target in ts .targets
165
182
if len (target .ids ) == 2 and ts .target_ordering == v2 .device_pb2 .TargetSet .SYMMETRIC
@@ -212,36 +229,30 @@ def validate_operation(self, operation: cirq.Operation) -> None:
212
229
raise ValueError (f'Qubit pair is not valid on device: { operation .qubits !r} ' )
213
230
214
231
def __str__ (self ) -> str :
215
- # If all qubits are grid qubits, render an appropriate text diagram.
216
- if all (isinstance (q , cirq .GridQubit ) for q in self ._metadata .qubit_set ):
217
- diagram = cirq .TextDiagramDrawer ()
232
+ diagram = cirq .TextDiagramDrawer ()
218
233
219
- qubits = cast (Set [cirq .GridQubit ], self ._metadata .qubit_set )
234
+ qubits = cast (Set [cirq .GridQubit ], self ._metadata .qubit_set )
220
235
221
- # Don't print out extras newlines if the row/col doesn't start at 0
222
- min_col = min (q .col for q in qubits )
223
- min_row = min (q .row for q in qubits )
236
+ # Don't print out extras newlines if the row/col doesn't start at 0
237
+ min_col = min (q .col for q in qubits )
238
+ min_row = min (q .row for q in qubits )
224
239
225
- for q in qubits :
226
- diagram .write (q .col - min_col , q .row - min_row , str (q ))
240
+ for q in qubits :
241
+ diagram .write (q .col - min_col , q .row - min_row , str (q ))
227
242
228
- # Find pairs that are connected by two-qubit gates.
229
- Pair = Tuple [cirq .GridQubit , cirq .GridQubit ]
230
- pairs = sorted ({cast (Pair , tuple (pair )) for pair in self ._metadata .qubit_pairs })
243
+ # Find pairs that are connected by two-qubit gates.
244
+ Pair = Tuple [cirq .GridQubit , cirq .GridQubit ]
245
+ pairs = sorted ({cast (Pair , tuple (pair )) for pair in self ._metadata .qubit_pairs })
231
246
232
- # Draw lines between connected pairs. Limit to horizontal/vertical
233
- # lines since that is all the diagram drawer can handle.
234
- for q1 , q2 in pairs :
235
- if q1 .row == q2 .row or q1 .col == q2 .col :
236
- diagram .grid_line (
237
- q1 .col - min_col , q1 .row - min_row , q2 .col - min_col , q2 .row - min_row
238
- )
247
+ # Draw lines between connected pairs. Limit to horizontal/vertical
248
+ # lines since that is all the diagram drawer can handle.
249
+ for q1 , q2 in pairs :
250
+ if q1 .row == q2 .row or q1 .col == q2 .col :
251
+ diagram .grid_line (
252
+ q1 .col - min_col , q1 .row - min_row , q2 .col - min_col , q2 .row - min_row
253
+ )
239
254
240
- return diagram .render (
241
- horizontal_spacing = 3 , vertical_spacing = 2 , use_unicode_characters = True
242
- )
243
-
244
- return super ().__str__ ()
255
+ return diagram .render (horizontal_spacing = 3 , vertical_spacing = 2 , use_unicode_characters = True )
245
256
246
257
def _repr_pretty_ (self , p : Any , cycle : bool ) -> None :
247
258
"""Creates ASCII diagram for Jupyter, IPython, etc."""
@@ -260,15 +271,3 @@ def _from_json_dict_(cls, metadata, **kwargs):
260
271
261
272
def _value_equality_values_ (self ):
262
273
return self ._metadata
263
-
264
-
265
- def _qid_from_str (id_str : str ) -> cirq .Qid :
266
- """Translates a qubit id string info cirq.Qid objects.
267
-
268
- Tries to translate to GridQubit if possible (e.g. '4_3'), otherwise
269
- falls back to using NamedQubit.
270
- """
271
- try :
272
- return v2 .grid_qubit_from_proto_id (id_str )
273
- except ValueError :
274
- return v2 .named_qubit_from_proto_id (id_str )
0 commit comments