diff --git a/cirq-core/cirq/circuits/circuit.py b/cirq-core/cirq/circuits/circuit.py index b60e1e0b080..5b3d64c9e55 100644 --- a/cirq-core/cirq/circuits/circuit.py +++ b/cirq-core/cirq/circuits/circuit.py @@ -67,6 +67,8 @@ CIRCUIT_TYPE = TypeVar('CIRCUIT_TYPE', bound='AbstractCircuit') INT_TYPE = Union[int, np.integer] +_DEVICE_DEP_MESSAGE = 'Attaching devices to circuits will no longer be supported.' + class Alignment(enum.Enum): # Stop when left ends are lined up. diff --git a/cirq-google/cirq_google/api/v1/programs.py b/cirq-google/cirq_google/api/v1/programs.py index 0b5fd34c44d..6ff037007b8 100644 --- a/cirq-google/cirq_google/api/v1/programs.py +++ b/cirq-google/cirq_google/api/v1/programs.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import json -from typing import Any, cast, Dict, Iterable, Optional, Sequence, Tuple, TYPE_CHECKING, Iterator +from typing import Any, cast, Dict, Optional, Sequence, Tuple, TYPE_CHECKING, Iterator import numpy as np import sympy @@ -166,16 +166,26 @@ def circuit_as_schedule_to_protos(circuit: cirq.Circuit) -> Iterator[operations_ yield op_proto -def circuit_from_schedule_from_protos( - device: 'cirq_google.XmonDevice', - ops: Iterable[operations_pb2.Operation], -) -> cirq.Circuit: - """Convert protos into a Circuit for the given device.""" +@cirq._compat.deprecated_parameter( + deadline='v0.15', + fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE, + parameter_desc='device', + match=lambda args, kwargs: 'device' in kwargs or len(args) > 1, +) +def circuit_from_schedule_from_protos(*args) -> cirq.Circuit: + """Convert protos into a Circuit.""" + if len(args) == 2: + device, ops = args[0], args[1] + else: + ops = args[0] result = [] for op in ops: xmon_op = xmon_op_from_proto(op) result.append(xmon_op) - return cirq.Circuit(result, device=device) + ret = cirq.Circuit(result) + if len(args) == 2: + ret._device = device + return ret def pack_results(measurements: Sequence[Tuple[str, np.ndarray]]) -> bytes: diff --git a/cirq-google/cirq_google/api/v1/programs_test.py b/cirq-google/cirq_google/api/v1/programs_test.py index 2efabbb50b3..4a1078c525e 100644 --- a/cirq-google/cirq_google/api/v1/programs_test.py +++ b/cirq-google/cirq_google/api/v1/programs_test.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import numpy as np import pytest import sympy @@ -32,14 +31,29 @@ def test_protobuf_round_trip(): circuit = cirq.Circuit( [cirq.X(q) ** 0.5 for q in device.qubits], [cirq.CZ(q, q2) for q in [cirq.GridQubit(0, 0)] for q2 in device.neighbors_of(q)], - device=device, ) protos = list(programs.circuit_as_schedule_to_protos(circuit)) - s2 = programs.circuit_from_schedule_from_protos(device, protos) + s2 = programs.circuit_from_schedule_from_protos(protos) assert s2 == circuit +def test_protobuf_round_trip_device_deprecated(): + device = cg.Foxtail + circuit = cirq.Circuit( + [cirq.X(q) ** 0.5 for q in device.qubits], + [cirq.CZ(q, q2) for q in [cirq.GridQubit(0, 0)] for q2 in device.neighbors_of(q)], + ) + circuit._device = device + + protos = list(programs.circuit_as_schedule_to_protos(circuit)) + with cirq.testing.assert_deprecated( + cirq.circuits.circuit._DEVICE_DEP_MESSAGE, deadline='v0.15' + ): + s2 = programs.circuit_from_schedule_from_protos(device, protos) + assert s2 == circuit + + def make_bytes(s: str) -> bytes: """Helper function to convert a string of digits into packed bytes. diff --git a/cirq-google/cirq_google/engine/engine.py b/cirq-google/cirq_google/engine/engine.py index 2beec954b0d..68a26278cc2 100644 --- a/cirq-google/cirq_google/engine/engine.py +++ b/cirq-google/cirq_google/engine/engine.py @@ -634,7 +634,6 @@ def _serialize_program( ) -> any_pb2.Any: if not isinstance(program, cirq.AbstractCircuit): raise TypeError(f'Unrecognized program type: {type(program)}') - program.device.validate_circuit(program) if self.context.proto_version == ProtoVersion.V2: program = gate_set.serialize(program) diff --git a/cirq-google/cirq_google/engine/engine_test.py b/cirq-google/cirq_google/engine/engine_test.py index cd848da175e..ecc4e4cdc46 100644 --- a/cirq-google/cirq_google/engine/engine_test.py +++ b/cirq-google/cirq_google/engine/engine_test.py @@ -392,21 +392,8 @@ def test_run_circuit(client): client.get_job_result.called_once_with() -def test_circuit_device_validation_fails(): - circuit = cirq.Circuit(device=cg.Foxtail) - - # Purposefully create an invalid Circuit by fiddling with internal bits. - # This simulates a failure in the incremental checks. - circuit._moments.append(cirq.Moment([cirq.Z(cirq.NamedQubit("dorothy"))])) - engine = cg.Engine(project_id='project-id') - with pytest.raises(ValueError, match='Unsupported qubit type'): - engine.run_sweep(program=circuit, gate_set=cg.XMON) - with pytest.raises(ValueError, match='Unsupported qubit type'): - engine.create_program(circuit, gate_set=cg.XMON) - - def test_no_gate_set(): - circuit = cirq.Circuit(device=cg.Sycamore) + circuit = cirq.Circuit() engine = cg.Engine(project_id='project-id') with pytest.raises(ValueError, match='No gate set'): engine.run(program=circuit) diff --git a/cirq-google/cirq_google/optimizers/optimize_for_sycamore.py b/cirq-google/cirq_google/optimizers/optimize_for_sycamore.py index 032be3c254f..3165ae0ee64 100644 --- a/cirq-google/cirq_google/optimizers/optimize_for_sycamore.py +++ b/cirq-google/cirq_google/optimizers/optimize_for_sycamore.py @@ -110,6 +110,12 @@ def _gate_product_tabulation_cached( raise NotImplementedError(f"Two qubit gate tabulation not supported for {optimizer_type}") +@cirq._compat.deprecated_parameter( + deadline='v0.15', + fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE, + parameter_desc='new_device', + match=lambda args, kwargs: 'new_device' in kwargs, +) def optimized_for_sycamore( circuit: cirq.Circuit, *, @@ -161,8 +167,9 @@ def optimized_for_sycamore( for optimizer in opts: optimizer(copy) - return cirq.Circuit( + ret = cirq.Circuit( (op.transform_qubits(qubit_map) for op in copy.all_operations()), strategy=cirq.InsertStrategy.EARLIEST, - device=new_device or copy.device, ) + ret._device = new_device or copy._device + return ret diff --git a/cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py b/cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py index cc383ca145f..9750ace13d2 100644 --- a/cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py +++ b/cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py @@ -116,3 +116,14 @@ def test_one_q_matrix_gate(): assert cg.SYC_GATESET.is_supported_operation(op) # single qubit gates shared between gatesets, so: assert cg.SQRT_ISWAP_GATESET.is_supported_operation(op) + + +def test_assert_new_device_deprecated(): + u = cirq.testing.random_special_unitary(2) + q = cirq.LineQubit(0) + circuit0 = cirq.Circuit(cirq.MatrixGate(u).on(q)) + _ = cg.optimized_for_sycamore(circuit0, optimizer_type='sqrt_iswap') + with cirq.testing.assert_deprecated( + cirq.circuits.circuit._DEVICE_DEP_MESSAGE, deadline='v0.15' + ): + _ = cg.optimized_for_sycamore(circuit0, optimizer_type='sqrt_iswap', new_device=cg.Foxtail) diff --git a/cirq-google/cirq_google/optimizers/optimize_for_xmon.py b/cirq-google/cirq_google/optimizers/optimize_for_xmon.py index 3cb0bc1141c..5bd9430622a 100644 --- a/cirq-google/cirq_google/optimizers/optimize_for_xmon.py +++ b/cirq-google/cirq_google/optimizers/optimize_for_xmon.py @@ -21,17 +21,19 @@ import cirq_google +@cirq._compat.deprecated_parameter( + deadline='v0.15', + fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE, + parameter_desc='new_device', + match=lambda args, kwargs: 'new_device' in kwargs, +) def optimized_for_xmon( circuit: cirq.Circuit, new_device: Optional['cirq_google.XmonDevice'] = None, qubit_map: Callable[[cirq.Qid], cirq.GridQubit] = lambda e: cast(cirq.GridQubit, e), allow_partial_czs: bool = False, ) -> cirq.Circuit: - if allow_partial_czs: - return optimized_for_sycamore( - circuit, new_device=new_device, qubit_map=qubit_map, optimizer_type='xmon_partial_cz' - ) - else: - return optimized_for_sycamore( - circuit, new_device=new_device, qubit_map=qubit_map, optimizer_type='xmon' - ) + optimizer_type = 'xmon_partial_cz' if allow_partial_czs else 'xmon' + ret = optimized_for_sycamore(circuit, qubit_map=qubit_map, optimizer_type=optimizer_type) + ret._device = new_device or circuit._device + return ret diff --git a/cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py b/cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py index 1507a8870f9..eed11197768 100644 --- a/cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py +++ b/cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - import pytest import cirq @@ -57,38 +56,13 @@ def test_ccz(): assert_circuits_with_terminal_measurements_are_equivalent(before, after, atol=1e-4) -def test_adjacent_cz_get_split_apart(): - before = cirq.Circuit( - [ - cirq.Moment( - [ - cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)), - cirq.CZ(cirq.GridQubit(1, 0), cirq.GridQubit(1, 1)), - ] - ) - ] - ) - - after = cg.optimized_for_xmon(before, new_device=cg.Foxtail) - - assert after == cirq.Circuit( - [ - cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1))]), - cirq.Moment([cirq.CZ(cirq.GridQubit(1, 0), cirq.GridQubit(1, 1))]), - ], - device=cg.Foxtail, - ) - - def test_remap_qubits(): before = cirq.Circuit([cirq.Moment([cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1))])]) - after = cg.optimized_for_xmon( - before, new_device=cg.Foxtail, qubit_map=lambda q: cirq.GridQubit(q.x, 0) - ) + after = cg.optimized_for_xmon(before, qubit_map=lambda q: cirq.GridQubit(q.x, 0)) assert after == cirq.Circuit( - [cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0))])], device=cg.Foxtail + [cirq.Moment([cirq.CZ(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0))])] ) diff --git a/cirq-google/cirq_google/serialization/gate_sets_test.py b/cirq-google/cirq_google/serialization/gate_sets_test.py index f7d3b0f865c..8899f599b61 100644 --- a/cirq-google/cirq_google/serialization/gate_sets_test.py +++ b/cirq-google/cirq_google/serialization/gate_sets_test.py @@ -11,8 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - +import os from typing import Dict +from unittest import mock import numpy as np import pytest import sympy @@ -355,7 +356,8 @@ def test_deserialize_circuit(): assert cg.XMON.deserialize(serialized) == circuit -def test_deserialize_schedule(): +@mock.patch.dict(os.environ, clear='CIRQ_TESTING') +def test_deserialize_schedule_device_deprecated(): q0 = cirq.GridQubit(4, 4) q1 = cirq.GridQubit(4, 5) circuit = cirq.Circuit( @@ -384,6 +386,33 @@ def test_deserialize_schedule(): assert cg.XMON.deserialize(serialized, cg.Bristlecone) == circuit +def test_deserialize_schedule(): + q0 = cirq.GridQubit(4, 4) + q1 = cirq.GridQubit(4, 5) + circuit = cirq.Circuit(cirq.CZ(q0, q1), cirq.X(q0), cirq.Z(q1), cirq.measure(q0, key='a')) + serialized = v2.program_pb2.Program( + language=v2.program_pb2.Language(gate_set='xmon'), + schedule=v2.program_pb2.Schedule( + scheduled_operations=[ + v2.program_pb2.ScheduledOperation( + operation=cg.XMON.serialize_op(cirq.CZ(q0, q1)), start_time_picos=0 + ), + v2.program_pb2.ScheduledOperation( + operation=cg.XMON.serialize_op(cirq.X(q0)), start_time_picos=200000 + ), + v2.program_pb2.ScheduledOperation( + operation=cg.XMON.serialize_op(cirq.Z(q1)), start_time_picos=200000 + ), + v2.program_pb2.ScheduledOperation( + operation=cg.XMON.serialize_op(cirq.measure(q0, key='a')), + start_time_picos=400000, + ), + ] + ), + ) + assert cg.XMON.deserialize(serialized) == circuit + + def test_serialize_deserialize_syc(): proto = op_proto({'gate': {'id': 'syc'}, 'args': {}, 'qubits': [{'id': '1_2'}, {'id': '1_3'}]}) diff --git a/cirq-google/cirq_google/serialization/serializable_gate_set.py b/cirq-google/cirq_google/serialization/serializable_gate_set.py index 6a9497e2cb1..985098b4eaa 100644 --- a/cirq-google/cirq_google/serialization/serializable_gate_set.py +++ b/cirq-google/cirq_google/serialization/serializable_gate_set.py @@ -273,6 +273,12 @@ def serialize_circuit_op( return proto_msg raise ValueError(f'Cannot serialize CircuitOperation {op!r}') + @cirq._compat.deprecated_parameter( + deadline='v0.15', + fix=cirq.circuits.circuit._DEVICE_DEP_MESSAGE, + parameter_desc='device', + match=lambda args, kwargs: 'device' in kwargs or len(args) > 2, + ) def deserialize( self, proto: v2.program_pb2.Program, device: Optional[cirq.Device] = None ) -> cirq.Circuit: @@ -322,10 +328,10 @@ def deserialize( constants=proto.constants, deserialized_constants=deserialized_constants, ) - return circuit if device is None else circuit.with_device(device) + if device is not None: + circuit._device = device # coverage: ignore + return circuit if which == 'schedule': - if device is None: - raise ValueError('Deserializing schedule requires a device but None was given.') return self._deserialize_schedule( proto.schedule, device, arg_function_language=proto.language.arg_function_language ) @@ -497,7 +503,7 @@ def _deserialize_circuit( def _deserialize_schedule( self, schedule_proto: v2.program_pb2.Schedule, - device: cirq.Device, + device: Optional[cirq.Device], *, arg_function_language: str, ) -> cirq.Circuit: @@ -510,4 +516,8 @@ def _deserialize_schedule( scheduled_op_proto.operation, arg_function_language=arg_function_language ) ) - return cirq.Circuit(result, device=device) + ret = cirq.Circuit(result) + if device is None: + device = cirq.UNCONSTRAINED_DEVICE + ret._device = device + return ret diff --git a/cirq-google/cirq_google/serialization/serializable_gate_set_test.py b/cirq-google/cirq_google/serialization/serializable_gate_set_test.py index 8d7d9689897..a55a50bbca7 100644 --- a/cirq-google/cirq_google/serialization/serializable_gate_set_test.py +++ b/cirq-google/cirq_google/serialization/serializable_gate_set_test.py @@ -387,29 +387,6 @@ def test_serialize_unrecognized(): MY_GATE_SET.serialize("not quite right") -def test_serialize_deserialize_schedule_no_device(): - q0 = cirq.GridQubit(1, 1) - q1 = cirq.GridQubit(1, 2) - proto = v2.program_pb2.Program( - language=v2.program_pb2.Language(arg_function_language='', gate_set='my_gate_set'), - schedule=v2.program_pb2.Schedule( - scheduled_operations=[ - v2.program_pb2.ScheduledOperation( - operation=X_SERIALIZER.to_proto(cirq.X(q0)), start_time_picos=0 - ), - v2.program_pb2.ScheduledOperation( - operation=X_SERIALIZER.to_proto(cirq.X(q1)), start_time_picos=200000 - ), - v2.program_pb2.ScheduledOperation( - operation=X_SERIALIZER.to_proto(cirq.X(q0)), start_time_picos=400000 - ), - ] - ), - ) - with pytest.raises(ValueError): - MY_GATE_SET.deserialize(proto) - - def test_serialize_deserialize_op(): q0 = cirq.GridQubit(1, 1) proto = op_proto( @@ -769,15 +746,6 @@ def test_deserialize_invalid_gate_set(): MY_GATE_SET.deserialize(proto) -def test_deserialize_schedule_missing_device(): - proto = v2.program_pb2.Program( - language=v2.program_pb2.Language(gate_set='my_gate_set'), - schedule=v2.program_pb2.Schedule(scheduled_operations=[]), - ) - with pytest.raises(ValueError, match='device'): - MY_GATE_SET.deserialize(proto) - - def test_deserialize_no_operation(): proto = v2.program_pb2.Program( language=v2.program_pb2.Language(gate_set='my_gate_set'), diff --git a/examples/advanced/quantum_volume.py b/examples/advanced/quantum_volume.py index 9924eed1ec0..b72f42b79fc 100644 --- a/examples/advanced/quantum_volume.py +++ b/examples/advanced/quantum_volume.py @@ -40,7 +40,7 @@ def main(*, num_qubits: int, depth: int, num_circuits: int, seed: int, routes: i Returns: Pass-through from calculate_quantum_volume. """ device = cirq_google.Bristlecone - compiler = lambda circuit: cirq_google.optimized_for_xmon(circuit=circuit, new_device=device) + compiler = lambda circuit: cirq_google.optimized_for_xmon(circuit=circuit) noisy = cirq.DensityMatrixSimulator( noise=cirq.ConstantQubitNoiseModel(qubit_noise_gate=cirq.DepolarizingChannel(p=0.005)) ) diff --git a/examples/place_on_bristlecone.py b/examples/place_on_bristlecone.py index 957609197a7..e25b36f0349 100644 --- a/examples/place_on_bristlecone.py +++ b/examples/place_on_bristlecone.py @@ -79,9 +79,7 @@ def main(): print() print("Xmon circuit:") - translated = cirq_google.optimized_for_xmon( - circuit=circuit, new_device=cirq_google.Bristlecone, qubit_map=lambda q: line[q.x] - ) + translated = cirq_google.optimized_for_xmon(circuit=circuit, qubit_map=lambda q: line[q.x]) print(translated)