12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
- """Device object representing Google devices."""
15
+ """Device object representing Google devices with a grid qubit layout ."""
16
16
17
- from typing import Any , Set , Tuple , cast
17
+ from typing import Any , List , Set , Tuple , cast
18
18
import cirq
19
19
from cirq_google .api import v2
20
20
21
21
22
+ def _get_qubit_pairs (proto : v2 .device_pb2 .DeviceSpecification ) -> List [Tuple [cirq .Qid , cirq .Qid ]]:
23
+ """Constructs a list of qubit pairs based on targets of symmetric 2-qubit gates in the proto."""
24
+
25
+ # While the `GateSpecification` proto message contains qubit target references, they are
26
+ # ignored here because the following assumptions make them unnecessary currently:
27
+ # * All valid qubit pairs work for all two-qubit gates.
28
+ # * All valid qubits work for all single-qubit gates.
29
+ # * Measurement gate can always be applied to all subset of qubits.
30
+ return [
31
+ (_qid_from_str (target .ids [0 ]), _qid_from_str (target .ids [1 ]))
32
+ for ts in proto .valid_targets
33
+ for target in ts .targets
34
+ if len (target .ids ) == 2 and ts .target_ordering == v2 .device_pb2 .TargetSet .SYMMETRIC
35
+ ]
36
+
37
+
38
+ def _validate_device_specification (proto : v2 .device_pb2 .DeviceSpecification ) -> None :
39
+ """Validates the DeviceSpecification proto.
40
+
41
+ Args:
42
+ proto: The DeviceSpecification proto to validate.
43
+
44
+ Raises:
45
+ ValueError: If the DeviceSpecification is invalid.
46
+
47
+ """
48
+
49
+ for target_set in proto .valid_targets :
50
+
51
+ # Check for unknown qubits in targets.
52
+ for target in target_set .targets :
53
+ for target_id in target .ids :
54
+ if target_id not in proto .valid_qubits :
55
+ raise ValueError (
56
+ f"Invalid DeviceSpecification: valid_targets contain qubit '{ target_id } '"
57
+ " which is not in valid_qubits."
58
+ )
59
+
60
+ # Symmetric and asymmetric targets should not have repeated qubits.
61
+ if (
62
+ target_set .target_ordering == v2 .device_pb2 .TargetSet .SYMMETRIC
63
+ or target_set .target_ordering == v2 .device_pb2 .TargetSet .ASYMMETRIC
64
+ ):
65
+ for target in target_set .targets :
66
+ if len (target .ids ) > len (set (target .ids )):
67
+ raise ValueError (
68
+ f"Invalid DeviceSpecification: the target set '{ target_set .name } ' is either"
69
+ " SYMMETRIC or ASYMMETRIC but has a target which contains repeated qubits:"
70
+ f" { target .ids } ."
71
+ )
72
+
73
+ # A SUBSET_PERMUTATION target should contain exactly one qubit.
74
+ if target_set .target_ordering == v2 .device_pb2 .TargetSet .SUBSET_PERMUTATION :
75
+ for target in target_set .targets :
76
+ if len (target .ids ) != 1 :
77
+ raise ValueError (
78
+ f"Invalid DeviceSpecification: the target set '{ target_set .name } ' is of"
79
+ " type SUBSET_PERMUTATION but contains a target which does not have exactly"
80
+ f" 1 qubit: { target .ids } ."
81
+ )
82
+
83
+
22
84
@cirq .value_equality
23
- class GoogleDevice (cirq .Device ):
24
- """Device object representing Google devices.
85
+ class GridDevice (cirq .Device ):
86
+ """Device object representing Google devices with a grid qubit layout .
25
87
26
88
For end users, instances of this class are typically accessed via
27
89
`Engine.get_processor('processor_name').get_device()`.
28
90
29
91
This class is compliant with the core `cirq.Device` abstraction. In particular:
30
92
* Device information is captured in the `metadata` property.
31
- * An instance of `GoogleDevice ` can be used to validate circuits, moments, and operations.
93
+ * An instance of `GridDevice ` can be used to validate circuits, moments, and operations.
32
94
33
95
Example use cases:
34
96
35
- * Get an instance of a Google device.
97
+ * Get an instance of a Google grid device.
36
98
>>> device = cirq_google.get_engine().get_processor('weber').get_device()
37
99
38
100
* Print the grid layout of the device.
@@ -78,15 +140,15 @@ class GoogleDevice(cirq.Device):
78
140
"""
79
141
80
142
def __init__ (self , metadata : cirq .GridDeviceMetadata ):
81
- """Creates a GoogleDevice object.
143
+ """Creates a GridDevice object.
82
144
83
145
This constructor typically should not be used directly. Use `from_proto()` instead.
84
146
"""
85
147
self ._metadata = metadata
86
148
87
149
@classmethod
88
- def from_proto (cls , proto : v2 .device_pb2 .DeviceSpecification ) -> 'GoogleDevice ' :
89
- """Create a `GoogleDevice ` from a DeviceSpecification proto.
150
+ def from_proto (cls , proto : v2 .device_pb2 .DeviceSpecification ) -> 'GridDevice ' :
151
+ """Create a `GridDevice ` from a DeviceSpecification proto.
90
152
91
153
This class only supports `cirq.GridQubit`s and `cirq.NamedQubit`s. If a
92
154
`DeviceSpecification.valid_qubits` string is in the form `<int>_<int>`, it is parsed as a
@@ -99,6 +161,8 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GoogleDevice':
99
161
ValueError: If the given `DeviceSpecification` is invalid.
100
162
"""
101
163
164
+ _validate_device_specification (proto )
165
+
102
166
# Create qubit set
103
167
all_qubits = [_qid_from_str (q ) for q in proto .valid_qubits ]
104
168
@@ -110,7 +174,6 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GoogleDevice':
110
174
# * All valid qubits work for all single-qubit gates.
111
175
# * Measurement gate can always be applied to all subset of qubits.
112
176
#
113
- # TODO(#5169) Consider adding the reversed pair, depending on the issue's solution.
114
177
qubit_pairs = [
115
178
(_qid_from_str (target .ids [0 ]), _qid_from_str (target .ids [1 ]))
116
179
for ts in proto .valid_targets
@@ -128,10 +191,10 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GoogleDevice':
128
191
except ValueError as ve :
129
192
raise ValueError ("DeviceSpecification is invalid." ) from ve
130
193
131
- return GoogleDevice (metadata )
194
+ return GridDevice (metadata )
132
195
133
196
@property
134
- def metadata (self ):
197
+ def metadata (self ) -> cirq . GridDeviceMetadata :
135
198
"""Get metadata information for the device."""
136
199
return self ._metadata
137
200
@@ -157,8 +220,10 @@ def validate_operation(self, operation: cirq.Operation) -> None:
157
220
if q not in self ._metadata .qubit_set :
158
221
raise ValueError (f'Qubit not on device: { q !r} ' )
159
222
160
- # TODO(#5169) May need to check the reverse pair depending on the issue's solution.
161
- if len (operation .qubits ) == 2 and tuple (operation .qubits ) not in self ._metadata .qubit_pairs :
223
+ if (
224
+ len (operation .qubits ) == 2
225
+ and frozenset (operation .qubits ) not in self ._metadata .qubit_pairs
226
+ ):
162
227
raise ValueError (f'Qubit pair is not valid on device: { operation .qubits !r} ' )
163
228
164
229
def __str__ (self ) -> str :
@@ -177,7 +242,7 @@ def __str__(self) -> str:
177
242
178
243
# Find pairs that are connected by two-qubit gates.
179
244
Pair = Tuple [cirq .GridQubit , cirq .GridQubit ]
180
- pairs = sorted ({cast (Pair , pair ) for pair in self ._metadata .qubit_pairs })
245
+ pairs = sorted ({cast (Pair , tuple ( pair ) ) for pair in self ._metadata .qubit_pairs })
181
246
182
247
# Draw lines between connected pairs. Limit to horizontal/vertical
183
248
# lines since that is all the diagram drawer can handle.
@@ -199,12 +264,10 @@ def _repr_pretty_(self, p: Any, cycle: bool) -> None:
199
264
p .text (repr (self ) if cycle else str (self ))
200
265
201
266
def __repr__ (self ) -> str :
202
- return f'cirq_google.GoogleDevice ({ repr (self ._metadata )} )'
267
+ return f'cirq_google.GridDevice ({ repr (self ._metadata )} )'
203
268
204
269
def _json_dict_ (self ):
205
- return {
206
- 'metadata' : self ._metadata ,
207
- }
270
+ return {'metadata' : self ._metadata }
208
271
209
272
@classmethod
210
273
def _from_json_dict_ (cls , metadata , ** kwargs ):
0 commit comments