Skip to content

Commit 5c9210f

Browse files
MichaelBroughtonrht
authored andcommitted
Remove use of .device in cirq-google. (quantumlib#4820)
Next step in quantumlib#4744 . Removes use of device attachment ability in cirq-google. This is a breaking change in the sense that some methods become "more forgiving" which I've highlighted below. Happy to add the tag if people think it's appropriate to label it.
1 parent 4cf7d18 commit 5c9210f

14 files changed

+117
-106
lines changed

cirq-core/cirq/circuits/circuit.py

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
CIRCUIT_TYPE = TypeVar('CIRCUIT_TYPE', bound='AbstractCircuit')
6868
INT_TYPE = Union[int, np.integer]
6969

70+
_DEVICE_DEP_MESSAGE = 'Attaching devices to circuits will no longer be supported.'
71+
7072

7173
class Alignment(enum.Enum):
7274
# Stop when left ends are lined up.

cirq-google/cirq_google/api/v1/programs.py

+17-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import json
15-
from typing import Any, cast, Dict, Iterable, Optional, Sequence, Tuple, TYPE_CHECKING, Iterator
15+
from typing import Any, cast, Dict, Optional, Sequence, Tuple, TYPE_CHECKING, Iterator
1616
import numpy as np
1717
import sympy
1818

@@ -166,16 +166,26 @@ def circuit_as_schedule_to_protos(circuit: cirq.Circuit) -> Iterator[operations_
166166
yield op_proto
167167

168168

169-
def circuit_from_schedule_from_protos(
170-
device: 'cirq_google.XmonDevice',
171-
ops: Iterable[operations_pb2.Operation],
172-
) -> cirq.Circuit:
173-
"""Convert protos into a Circuit for the given device."""
169+
@cirq._compat.deprecated_parameter(
170+
deadline='v0.15',
171+
fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE,
172+
parameter_desc='device',
173+
match=lambda args, kwargs: 'device' in kwargs or len(args) > 1,
174+
)
175+
def circuit_from_schedule_from_protos(*args) -> cirq.Circuit:
176+
"""Convert protos into a Circuit."""
177+
if len(args) == 2:
178+
device, ops = args[0], args[1]
179+
else:
180+
ops = args[0]
174181
result = []
175182
for op in ops:
176183
xmon_op = xmon_op_from_proto(op)
177184
result.append(xmon_op)
178-
return cirq.Circuit(result, device=device)
185+
ret = cirq.Circuit(result)
186+
if len(args) == 2:
187+
ret._device = device
188+
return ret
179189

180190

181191
def pack_results(measurements: Sequence[Tuple[str, np.ndarray]]) -> bytes:

cirq-google/cirq_google/api/v1/programs_test.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
1514
import numpy as np
1615
import pytest
1716
import sympy
@@ -32,14 +31,29 @@ def test_protobuf_round_trip():
3231
circuit = cirq.Circuit(
3332
[cirq.X(q) ** 0.5 for q in device.qubits],
3433
[cirq.CZ(q, q2) for q in [cirq.GridQubit(0, 0)] for q2 in device.neighbors_of(q)],
35-
device=device,
3634
)
3735

3836
protos = list(programs.circuit_as_schedule_to_protos(circuit))
39-
s2 = programs.circuit_from_schedule_from_protos(device, protos)
37+
s2 = programs.circuit_from_schedule_from_protos(protos)
4038
assert s2 == circuit
4139

4240

41+
def test_protobuf_round_trip_device_deprecated():
42+
device = cg.Foxtail
43+
circuit = cirq.Circuit(
44+
[cirq.X(q) ** 0.5 for q in device.qubits],
45+
[cirq.CZ(q, q2) for q in [cirq.GridQubit(0, 0)] for q2 in device.neighbors_of(q)],
46+
)
47+
circuit._device = device
48+
49+
protos = list(programs.circuit_as_schedule_to_protos(circuit))
50+
with cirq.testing.assert_deprecated(
51+
cirq.circuits.circuit._DEVICE_DEP_MESSAGE, deadline='v0.15'
52+
):
53+
s2 = programs.circuit_from_schedule_from_protos(device, protos)
54+
assert s2 == circuit
55+
56+
4357
def make_bytes(s: str) -> bytes:
4458
"""Helper function to convert a string of digits into packed bytes.
4559

cirq-google/cirq_google/engine/engine.py

-1
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,6 @@ def _serialize_program(
634634
) -> any_pb2.Any:
635635
if not isinstance(program, cirq.AbstractCircuit):
636636
raise TypeError(f'Unrecognized program type: {type(program)}')
637-
program.device.validate_circuit(program)
638637

639638
if self.context.proto_version == ProtoVersion.V2:
640639
program = gate_set.serialize(program)

cirq-google/cirq_google/engine/engine_test.py

+1-14
Original file line numberDiff line numberDiff line change
@@ -392,21 +392,8 @@ def test_run_circuit(client):
392392
client.get_job_result.called_once_with()
393393

394394

395-
def test_circuit_device_validation_fails():
396-
circuit = cirq.Circuit(device=cg.Foxtail)
397-
398-
# Purposefully create an invalid Circuit by fiddling with internal bits.
399-
# This simulates a failure in the incremental checks.
400-
circuit._moments.append(cirq.Moment([cirq.Z(cirq.NamedQubit("dorothy"))]))
401-
engine = cg.Engine(project_id='project-id')
402-
with pytest.raises(ValueError, match='Unsupported qubit type'):
403-
engine.run_sweep(program=circuit, gate_set=cg.XMON)
404-
with pytest.raises(ValueError, match='Unsupported qubit type'):
405-
engine.create_program(circuit, gate_set=cg.XMON)
406-
407-
408395
def test_no_gate_set():
409-
circuit = cirq.Circuit(device=cg.Sycamore)
396+
circuit = cirq.Circuit()
410397
engine = cg.Engine(project_id='project-id')
411398
with pytest.raises(ValueError, match='No gate set'):
412399
engine.run(program=circuit)

cirq-google/cirq_google/optimizers/optimize_for_sycamore.py

+9-2
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ def _gate_product_tabulation_cached(
110110
raise NotImplementedError(f"Two qubit gate tabulation not supported for {optimizer_type}")
111111

112112

113+
@cirq._compat.deprecated_parameter(
114+
deadline='v0.15',
115+
fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE,
116+
parameter_desc='new_device',
117+
match=lambda args, kwargs: 'new_device' in kwargs,
118+
)
113119
def optimized_for_sycamore(
114120
circuit: cirq.Circuit,
115121
*,
@@ -161,8 +167,9 @@ def optimized_for_sycamore(
161167
for optimizer in opts:
162168
optimizer(copy)
163169

164-
return cirq.Circuit(
170+
ret = cirq.Circuit(
165171
(op.transform_qubits(qubit_map) for op in copy.all_operations()),
166172
strategy=cirq.InsertStrategy.EARLIEST,
167-
device=new_device or copy.device,
168173
)
174+
ret._device = new_device or copy._device
175+
return ret

cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py

+11
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,14 @@ def test_one_q_matrix_gate():
116116
assert cg.SYC_GATESET.is_supported_operation(op)
117117
# single qubit gates shared between gatesets, so:
118118
assert cg.SQRT_ISWAP_GATESET.is_supported_operation(op)
119+
120+
121+
def test_assert_new_device_deprecated():
122+
u = cirq.testing.random_special_unitary(2)
123+
q = cirq.LineQubit(0)
124+
circuit0 = cirq.Circuit(cirq.MatrixGate(u).on(q))
125+
_ = cg.optimized_for_sycamore(circuit0, optimizer_type='sqrt_iswap')
126+
with cirq.testing.assert_deprecated(
127+
cirq.circuits.circuit._DEVICE_DEP_MESSAGE, deadline='v0.15'
128+
):
129+
_ = cg.optimized_for_sycamore(circuit0, optimizer_type='sqrt_iswap', new_device=cg.Foxtail)

cirq-google/cirq_google/optimizers/optimize_for_xmon.py

+10-8
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,19 @@
2121
import cirq_google
2222

2323

24+
@cirq._compat.deprecated_parameter(
25+
deadline='v0.15',
26+
fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE,
27+
parameter_desc='new_device',
28+
match=lambda args, kwargs: 'new_device' in kwargs,
29+
)
2430
def optimized_for_xmon(
2531
circuit: cirq.Circuit,
2632
new_device: Optional['cirq_google.XmonDevice'] = None,
2733
qubit_map: Callable[[cirq.Qid], cirq.GridQubit] = lambda e: cast(cirq.GridQubit, e),
2834
allow_partial_czs: bool = False,
2935
) -> cirq.Circuit:
30-
if allow_partial_czs:
31-
return optimized_for_sycamore(
32-
circuit, new_device=new_device, qubit_map=qubit_map, optimizer_type='xmon_partial_cz'
33-
)
34-
else:
35-
return optimized_for_sycamore(
36-
circuit, new_device=new_device, qubit_map=qubit_map, optimizer_type='xmon'
37-
)
36+
optimizer_type = 'xmon_partial_cz' if allow_partial_czs else 'xmon'
37+
ret = optimized_for_sycamore(circuit, qubit_map=qubit_map, optimizer_type=optimizer_type)
38+
ret._device = new_device or circuit._device
39+
return ret

cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py

+2-28
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
1514
import pytest
1615

1716
import cirq
@@ -57,38 +56,13 @@ def test_ccz():
5756
assert_circuits_with_terminal_measurements_are_equivalent(before, after, atol=1e-4)
5857

5958

60-
def test_adjacent_cz_get_split_apart():
61-
before = cirq.Circuit(
62-
[
63-
cirq.Moment(
64-
[
65-
cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)),
66-
cirq.CZ(cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)),
67-
]
68-
)
69-
]
70-
)
71-
72-
after = cg.optimized_for_xmon(before, new_device=cg.Foxtail)
73-
74-
assert after == cirq.Circuit(
75-
[
76-
cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1))]),
77-
cirq.Moment([cirq.CZ(cirq.GridQubit(1, 0), cirq.GridQubit(1, 1))]),
78-
],
79-
device=cg.Foxtail,
80-
)
81-
82-
8359
def test_remap_qubits():
8460
before = cirq.Circuit([cirq.Moment([cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1))])])
8561

86-
after = cg.optimized_for_xmon(
87-
before, new_device=cg.Foxtail, qubit_map=lambda q: cirq.GridQubit(q.x, 0)
88-
)
62+
after = cg.optimized_for_xmon(before, qubit_map=lambda q: cirq.GridQubit(q.x, 0))
8963

9064
assert after == cirq.Circuit(
91-
[cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0))])], device=cg.Foxtail
65+
[cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0))])]
9266
)
9367

9468

cirq-google/cirq_google/serialization/gate_sets_test.py

+31-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
14+
import os
1515
from typing import Dict
16+
from unittest import mock
1617
import numpy as np
1718
import pytest
1819
import sympy
@@ -355,7 +356,8 @@ def test_deserialize_circuit():
355356
assert cg.XMON.deserialize(serialized) == circuit
356357

357358

358-
def test_deserialize_schedule():
359+
@mock.patch.dict(os.environ, clear='CIRQ_TESTING')
360+
def test_deserialize_schedule_device_deprecated():
359361
q0 = cirq.GridQubit(4, 4)
360362
q1 = cirq.GridQubit(4, 5)
361363
circuit = cirq.Circuit(
@@ -384,6 +386,33 @@ def test_deserialize_schedule():
384386
assert cg.XMON.deserialize(serialized, cg.Bristlecone) == circuit
385387

386388

389+
def test_deserialize_schedule():
390+
q0 = cirq.GridQubit(4, 4)
391+
q1 = cirq.GridQubit(4, 5)
392+
circuit = cirq.Circuit(cirq.CZ(q0, q1), cirq.X(q0), cirq.Z(q1), cirq.measure(q0, key='a'))
393+
serialized = v2.program_pb2.Program(
394+
language=v2.program_pb2.Language(gate_set='xmon'),
395+
schedule=v2.program_pb2.Schedule(
396+
scheduled_operations=[
397+
v2.program_pb2.ScheduledOperation(
398+
operation=cg.XMON.serialize_op(cirq.CZ(q0, q1)), start_time_picos=0
399+
),
400+
v2.program_pb2.ScheduledOperation(
401+
operation=cg.XMON.serialize_op(cirq.X(q0)), start_time_picos=200000
402+
),
403+
v2.program_pb2.ScheduledOperation(
404+
operation=cg.XMON.serialize_op(cirq.Z(q1)), start_time_picos=200000
405+
),
406+
v2.program_pb2.ScheduledOperation(
407+
operation=cg.XMON.serialize_op(cirq.measure(q0, key='a')),
408+
start_time_picos=400000,
409+
),
410+
]
411+
),
412+
)
413+
assert cg.XMON.deserialize(serialized) == circuit
414+
415+
387416
def test_serialize_deserialize_syc():
388417
proto = op_proto({'gate': {'id': 'syc'}, 'args': {}, 'qubits': [{'id': '1_2'}, {'id': '1_3'}]})
389418

cirq-google/cirq_google/serialization/serializable_gate_set.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,12 @@ def serialize_circuit_op(
273273
return proto_msg
274274
raise ValueError(f'Cannot serialize CircuitOperation {op!r}')
275275

276+
@cirq._compat.deprecated_parameter(
277+
deadline='v0.15',
278+
fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE,
279+
parameter_desc='device',
280+
match=lambda args, kwargs: 'device' in kwargs or len(args) > 2,
281+
)
276282
def deserialize(
277283
self, proto: v2.program_pb2.Program, device: Optional[cirq.Device] = None
278284
) -> cirq.Circuit:
@@ -322,10 +328,10 @@ def deserialize(
322328
constants=proto.constants,
323329
deserialized_constants=deserialized_constants,
324330
)
325-
return circuit if device is None else circuit.with_device(device)
331+
if device is not None:
332+
circuit._device = device # coverage: ignore
333+
return circuit
326334
if which == 'schedule':
327-
if device is None:
328-
raise ValueError('Deserializing schedule requires a device but None was given.')
329335
return self._deserialize_schedule(
330336
proto.schedule, device, arg_function_language=proto.language.arg_function_language
331337
)
@@ -497,7 +503,7 @@ def _deserialize_circuit(
497503
def _deserialize_schedule(
498504
self,
499505
schedule_proto: v2.program_pb2.Schedule,
500-
device: cirq.Device,
506+
device: Optional[cirq.Device],
501507
*,
502508
arg_function_language: str,
503509
) -> cirq.Circuit:
@@ -510,4 +516,8 @@ def _deserialize_schedule(
510516
scheduled_op_proto.operation, arg_function_language=arg_function_language
511517
)
512518
)
513-
return cirq.Circuit(result, device=device)
519+
ret = cirq.Circuit(result)
520+
if device is None:
521+
device = cirq.UNCONSTRAINED_DEVICE
522+
ret._device = device
523+
return ret

cirq-google/cirq_google/serialization/serializable_gate_set_test.py

-32
Original file line numberDiff line numberDiff line change
@@ -387,29 +387,6 @@ def test_serialize_unrecognized():
387387
MY_GATE_SET.serialize("not quite right")
388388

389389

390-
def test_serialize_deserialize_schedule_no_device():
391-
q0 = cirq.GridQubit(1, 1)
392-
q1 = cirq.GridQubit(1, 2)
393-
proto = v2.program_pb2.Program(
394-
language=v2.program_pb2.Language(arg_function_language='', gate_set='my_gate_set'),
395-
schedule=v2.program_pb2.Schedule(
396-
scheduled_operations=[
397-
v2.program_pb2.ScheduledOperation(
398-
operation=X_SERIALIZER.to_proto(cirq.X(q0)), start_time_picos=0
399-
),
400-
v2.program_pb2.ScheduledOperation(
401-
operation=X_SERIALIZER.to_proto(cirq.X(q1)), start_time_picos=200000
402-
),
403-
v2.program_pb2.ScheduledOperation(
404-
operation=X_SERIALIZER.to_proto(cirq.X(q0)), start_time_picos=400000
405-
),
406-
]
407-
),
408-
)
409-
with pytest.raises(ValueError):
410-
MY_GATE_SET.deserialize(proto)
411-
412-
413390
def test_serialize_deserialize_op():
414391
q0 = cirq.GridQubit(1, 1)
415392
proto = op_proto(
@@ -769,15 +746,6 @@ def test_deserialize_invalid_gate_set():
769746
MY_GATE_SET.deserialize(proto)
770747

771748

772-
def test_deserialize_schedule_missing_device():
773-
proto = v2.program_pb2.Program(
774-
language=v2.program_pb2.Language(gate_set='my_gate_set'),
775-
schedule=v2.program_pb2.Schedule(scheduled_operations=[]),
776-
)
777-
with pytest.raises(ValueError, match='device'):
778-
MY_GATE_SET.deserialize(proto)
779-
780-
781749
def test_deserialize_no_operation():
782750
proto = v2.program_pb2.Program(
783751
language=v2.program_pb2.Language(gate_set='my_gate_set'),

examples/advanced/quantum_volume.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def main(*, num_qubits: int, depth: int, num_circuits: int, seed: int, routes: i
4040
Returns: Pass-through from calculate_quantum_volume.
4141
"""
4242
device = cirq_google.Bristlecone
43-
compiler = lambda circuit: cirq_google.optimized_for_xmon(circuit=circuit, new_device=device)
43+
compiler = lambda circuit: cirq_google.optimized_for_xmon(circuit=circuit)
4444
noisy = cirq.DensityMatrixSimulator(
4545
noise=cirq.ConstantQubitNoiseModel(qubit_noise_gate=cirq.DepolarizingChannel(p=0.005))
4646
)

0 commit comments

Comments
 (0)