Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove use of .device in cirq-google. #4820

Merged
merged 8 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
24 changes: 17 additions & 7 deletions cirq-google/cirq_google/api/v1/programs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
20 changes: 17 additions & 3 deletions cirq-google/cirq_google/api/v1/programs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.

Expand Down
1 change: 0 additions & 1 deletion cirq-google/cirq_google/engine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Engine no longer validates inside of serialization. As we know this already wasn't doing a great job of catching all the breaks that might come over through engine. Would probably make more sense to couple this up with your new work on the engine emulator. What are your thoughts here @dstrain115 ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay, but we should probably modify documentation to make sure that people validate independently of the serialization then. Maybe we could add this to the cirq 1.0 doc list?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to the list thx.


if self.context.proto_version == ProtoVersion.V2:
program = gate_set.serialize(program)
Expand Down
15 changes: 1 addition & 14 deletions cirq-google/cirq_google/engine/engine_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 9 additions & 2 deletions cirq-google/cirq_google/optimizers/optimize_for_sycamore.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
*,
Expand Down Expand Up @@ -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
11 changes: 11 additions & 0 deletions cirq-google/cirq_google/optimizers/optimize_for_sycamore_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
18 changes: 10 additions & 8 deletions cirq-google/cirq_google/optimizers/optimize_for_xmon.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plan to deprecate the device function signature from this optimizer because it is not a device aware optimizer and all the device was used for was attaching it to the returned circuit.

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
30 changes: 2 additions & 28 deletions cirq-google/cirq_google/optimizers/optimize_for_xmon_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))])]
)


Expand Down
33 changes: 31 additions & 2 deletions cirq-google/cirq_google/serialization/gate_sets_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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'}]})

Expand Down
20 changes: 15 additions & 5 deletions cirq-google/cirq_google/serialization/serializable_gate_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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.')
Comment on lines -327 to -328
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a break here that schedules can now have no device. Since we aren't using these anymore I figured this was an acceptable vendor break. @dstrain115

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Schedule has been deprecated for over 2.5 years, so we don't need to worry about breakage here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SG

return self._deserialize_schedule(
proto.schedule, device, arg_function_language=proto.language.arg_function_language
)
Expand Down Expand Up @@ -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:
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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'),
Expand Down
2 changes: 1 addition & 1 deletion examples/advanced/quantum_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
)
Expand Down
Loading