Skip to content

Commit 9f3afb5

Browse files
committed
Addressed Doug's comments
1 parent 5c75527 commit 9f3afb5

File tree

4 files changed

+72
-59
lines changed

4 files changed

+72
-59
lines changed

cirq-core/cirq/devices/grid_device_metadata.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,6 @@ def _from_json_dict_(cls, qubit_pairs, gateset, gate_durations, all_qubits, **kw
170170
return cls(
171171
qubit_pairs,
172172
gateset,
173-
None if gate_durations is None else dict(gate_durations),
173+
dict(gate_durations) if gate_durations is not None else None,
174174
all_qubits,
175175
)

cirq-google/cirq_google/api/v2/device.proto

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ message DeviceSpecification {
2222
// A list of allowed ids for qubits within the Program.
2323
// Any programs with ids not in this list will be rejected.
2424
// If empty, all qubit values are allowed (e.g. in a simulator)
25+
// Only grid qubits are supported. Strings must be in the form '<int>_<int>'.
2526
repeated string valid_qubits = 2;
2627

2728
// A list of targets that gates can use.

cirq-google/cirq_google/devices/grid_device.py

+47-48
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
"""Device object representing Google devices with a grid qubit layout."""
1616

17+
import re
18+
1719
from typing import Any, Set, Tuple, cast
1820
import cirq
1921
from cirq_google.api import v2
@@ -27,9 +29,16 @@ def _validate_device_specification(proto: v2.device_pb2.DeviceSpecification) ->
2729
2830
Raises:
2931
ValueError: If the DeviceSpecification is invalid.
30-
3132
"""
3233

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+
3342
for target_set in proto.valid_targets:
3443

3544
# Check for unknown qubits in targets.
@@ -79,16 +88,16 @@ class GridDevice(cirq.Device):
7988
Example use cases:
8089
8190
* 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()
8392
8493
* Print the grid layout of the device.
8594
>>> print(device)
8695
8796
* 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.
8998
9099
* 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.
92101
93102
* Get the `cirq.Gateset` containing valid gates for the device, and inspect the full list
94103
of valid gates.
@@ -116,7 +125,7 @@ class GridDevice(cirq.Device):
116125
117126
For Google devices, the
118127
[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
120129
)
121130
is the main specification for device information surfaced by the Quantum Computing Service.
122131
Thus, this class is should be instantiated using a `DeviceSpecification` proto via the
@@ -134,21 +143,29 @@ def __init__(self, metadata: cirq.GridDeviceMetadata):
134143
def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GridDevice':
135144
"""Create a `GridDevice` from a DeviceSpecification proto.
136145
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-
141146
Args:
142147
proto: The `DeviceSpecification` proto describing a Google device.
143148
144149
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+
146163
"""
147164

148165
_validate_device_specification(proto)
149166

150167
# 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]
152169

153170
# Create qubit pair set
154171
#
@@ -159,7 +176,7 @@ def from_proto(cls, proto: v2.device_pb2.DeviceSpecification) -> 'GridDevice':
159176
# * Measurement gate can always be applied to all subset of qubits.
160177
#
161178
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]))
163180
for ts in proto.valid_targets
164181
for target in ts.targets
165182
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:
212229
raise ValueError(f'Qubit pair is not valid on device: {operation.qubits!r}')
213230

214231
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()
218233

219-
qubits = cast(Set[cirq.GridQubit], self._metadata.qubit_set)
234+
qubits = cast(Set[cirq.GridQubit], self._metadata.qubit_set)
220235

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)
224239

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))
227242

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})
231246

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+
)
239254

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)
245256

246257
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
247258
"""Creates ASCII diagram for Jupyter, IPython, etc."""
@@ -260,15 +271,3 @@ def _from_json_dict_(cls, metadata, **kwargs):
260271

261272
def _value_equality_values_(self):
262273
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)

cirq-google/cirq_google/devices/grid_device_test.py

+23-10
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,20 @@ def _create_device_spec_with_all_couplings():
8080
return grid_qubits, spec
8181

8282

83+
def _create_device_spec_invalid_qubit_name() -> v2.device_pb2.DeviceSpecification:
84+
"""Creates a DeviceSpecification with a qubit name that does not conform to '<int>_<int>'."""
85+
q_proto_id = v2.qubit_to_proto_id(cirq.NamedQubit('q0_0'))
86+
87+
spec = v2.device_pb2.DeviceSpecification()
88+
spec.valid_qubits.extend([q_proto_id])
89+
90+
return spec
91+
92+
8393
def _create_device_spec_qubit_pair_self_loops() -> v2.device_pb2.DeviceSpecification:
84-
q_proto_id = v2.qubit_to_proto_id(cirq.NamedQubit('q'))
94+
"""Creates an invalid DeviceSpecification with a qubit pair ('0_0', '0_0')."""
95+
96+
q_proto_id = v2.qubit_to_proto_id(cirq.GridQubit(0, 0))
8597

8698
spec = v2.device_pb2.DeviceSpecification()
8799
spec.valid_qubits.extend([q_proto_id])
@@ -95,6 +107,8 @@ def _create_device_spec_qubit_pair_self_loops() -> v2.device_pb2.DeviceSpecifica
95107

96108

97109
def _create_device_spec_invalid_qubit_in_qubit_pair() -> v2.device_pb2.DeviceSpecification:
110+
"""Creates a DeviceSpecification where qubit '0_1' is in a pair but not in valid_qubits."""
111+
98112
q_proto_ids = [v2.qubit_to_proto_id(cirq.GridQubit(0, i)) for i in range(2)]
99113

100114
spec = v2.device_pb2.DeviceSpecification()
@@ -109,6 +123,8 @@ def _create_device_spec_invalid_qubit_in_qubit_pair() -> v2.device_pb2.DeviceSpe
109123

110124

111125
def _create_device_spec_invalid_subset_permutation_target() -> v2.device_pb2.DeviceSpecification:
126+
"""Creates a DeviceSpecification where a SUBSET_PERMUTATION target contains 2 qubits."""
127+
112128
q_proto_ids = [v2.qubit_to_proto_id(cirq.GridQubit(0, i)) for i in range(2)]
113129

114130
spec = v2.device_pb2.DeviceSpecification()
@@ -122,7 +138,7 @@ def _create_device_spec_invalid_subset_permutation_target() -> v2.device_pb2.Dev
122138
return spec
123139

124140

125-
def test_grid_device_from_proto_and_validation():
141+
def test_grid_device_from_proto():
126142
grid_qubits, spec = _create_device_spec_with_horizontal_couplings()
127143

128144
device = cirq_google.GridDevice.from_proto(spec)
@@ -165,6 +181,11 @@ def test_grid_device_validate_operations_negative():
165181
# TODO(#5050) verify validate_operations gateset errors
166182

167183

184+
def test_grid_device_invalid_qubit_name():
185+
with pytest.raises(ValueError, match='not in the GridQubit form'):
186+
cirq_google.GridDevice.from_proto(_create_device_spec_invalid_qubit_name())
187+
188+
168189
def test_grid_device_invalid_qubit_in_qubit_pair():
169190
with pytest.raises(ValueError, match='which is not in valid_qubits'):
170191
cirq_google.GridDevice.from_proto(_create_device_spec_invalid_qubit_in_qubit_pair())
@@ -218,11 +239,3 @@ def test_grid_device_repr_pretty(cycle, func):
218239
printer = mock.Mock()
219240
device._repr_pretty_(printer, cycle)
220241
printer.text.assert_called_once_with(func(device))
221-
222-
223-
def test_grid_device_str_named_qubits():
224-
q_proto_id = v2.qubit_to_proto_id(cirq.NamedQubit('q'))
225-
spec = v2.device_pb2.DeviceSpecification()
226-
spec.valid_qubits.extend([q_proto_id])
227-
device = cirq_google.GridDevice.from_proto(spec)
228-
assert device.__class__.__name__ in str(device)

0 commit comments

Comments
 (0)