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

Deprecate ciruit.device everywhere. #4845

Merged
merged 7 commits into from
Jan 19, 2022
Merged
Show file tree
Hide file tree
Changes from 6 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
138 changes: 103 additions & 35 deletions cirq-core/cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@
"""

import abc
import contextlib
import enum
import html
import itertools
import math
import re
import warnings
from collections import defaultdict
from typing import (
AbstractSet,
Expand All @@ -50,7 +53,7 @@
import numpy as np

import cirq._version
from cirq import devices, ops, protocols, qis
from cirq import _compat, devices, ops, protocols, qis
from cirq.circuits._bucket_priority_queue import BucketPriorityQueue
from cirq.circuits.circuit_operation import CircuitOperation
from cirq.circuits.insert_strategy import InsertStrategy
Expand All @@ -70,6 +73,17 @@
_DEVICE_DEP_MESSAGE = 'Attaching devices to circuits will no longer be supported.'


@contextlib.contextmanager
def _block_overlapping_deprecation():
with warnings.catch_warnings():
warnings.filterwarnings(
action='ignore',
category=DeprecationWarning,
message=f'(.|\n)*{re.escape(_DEVICE_DEP_MESSAGE)}(.|\n)*',
)
yield


class Alignment(enum.Enum):
# Stop when left ends are lined up.
LEFT = 1
Expand Down Expand Up @@ -139,7 +153,10 @@ def freeze(self) -> 'cirq.FrozenCircuit':

if isinstance(self, FrozenCircuit):
return self
return FrozenCircuit(self, strategy=InsertStrategy.EARLIEST, device=self.device)

if self._device == cirq.UNCONSTRAINED_DEVICE:
return FrozenCircuit(self, strategy=InsertStrategy.EARLIEST)
return FrozenCircuit(self, strategy=InsertStrategy.EARLIEST, device=self._device)
Comment on lines +157 to +159
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason I opted for this patter instead of something like:

ret_device = FrozenCircuit(self, strategy=....)
ret_device._device = self._device
return ret_device

Is because the constructor with the device argument makes non trivial behavior changes to the constructor and so that's why I opted to use this pattern in places with deprecations. Removing them once the cycle is over should be relatively easy to do since all we need to do is keep the snippet inside the if statement and delete the else block.


def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
"""Creates a Circuit from this circuit.
Expand All @@ -149,23 +166,26 @@ def unfreeze(self, copy: bool = True) -> 'cirq.Circuit':
"""
if isinstance(self, Circuit):
return Circuit.copy(self) if copy else self
return Circuit(self, strategy=InsertStrategy.EARLIEST, device=self.device)

if self._device == cirq.UNCONSTRAINED_DEVICE:
return Circuit(self, strategy=InsertStrategy.EARLIEST)
return Circuit(self, strategy=InsertStrategy.EARLIEST, device=self._device)

def __bool__(self):
return bool(self.moments)

def __eq__(self, other):
if not isinstance(other, AbstractCircuit):
return NotImplemented
return tuple(self.moments) == tuple(other.moments) and self.device == other.device
return tuple(self.moments) == tuple(other.moments) and self._device == other._device

def _approx_eq_(self, other: Any, atol: Union[int, float]) -> bool:
"""See `cirq.protocols.SupportsApproximateEquality`."""
if not isinstance(other, AbstractCircuit):
return NotImplemented
return (
cirq.protocols.approx_eq(tuple(self.moments), tuple(other.moments), atol=atol)
and self.device == other.device
and self._device == other._device
)

def __ne__(self, other) -> bool:
Expand Down Expand Up @@ -241,7 +261,7 @@ def __repr__(self) -> str:
args = []
if self.moments:
args.append(_list_repr_with_indented_item_lines(self.moments))
if self.device != devices.UNCONSTRAINED_DEVICE:
if self._device != devices.UNCONSTRAINED_DEVICE:
args.append(f'device={self.device!r}')
return f'cirq.{cls_name}({", ".join(args)})'

Expand Down Expand Up @@ -1323,11 +1343,16 @@ def save_qasm(
self._to_qasm_output(header, precision, qubit_order).save(file_path)

def _json_dict_(self):
return protocols.obj_to_dict_helper(self, ['moments', 'device'])
ret = protocols.obj_to_dict_helper(self, ['moments', '_device'])
ret['device'] = ret['_device']
del ret['_device']
return ret

@classmethod
def _from_json_dict_(cls, moments, device, **kwargs):
return cls(moments, strategy=InsertStrategy.EARLIEST, device=device)
if device == cirq.UNCONSTRAINED_DEVICE:
return cls(moments, strategy=InsertStrategy.EARLIEST)
return cls(moments, device=device, strategy=InsertStrategy.EARLIEST)

def zip(
*circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
Expand Down Expand Up @@ -1677,6 +1702,12 @@ class Circuit(AbstractCircuit):
independent 'factors' of the original Circuit.
"""

@_compat.deprecated_parameter(
deadline='v0.15',
fix=_DEVICE_DEP_MESSAGE,
parameter_desc='device',
match=lambda args, kwargs: 'device' in kwargs,
)
def __init__(
self,
*contents: 'cirq.OP_TREE',
Expand All @@ -1701,25 +1732,40 @@ def __init__(
self._device = device
self.append(contents, strategy=strategy)

@property
def device(self) -> 'cirq.Device':
@property # type: ignore
@_compat.deprecated(
deadline='v0.15',
fix=_DEVICE_DEP_MESSAGE,
)
def device(self) -> devices.Device:
return self._device

@device.setter
@device.setter # type: ignore
@_compat.deprecated(
deadline='v0.15',
fix=_DEVICE_DEP_MESSAGE,
)
def device(self, new_device: 'cirq.Device') -> None:
new_device.validate_circuit(self)
self._device = new_device

def __copy__(self) -> 'cirq.Circuit':
return self.copy()

def copy(self) -> 'cirq.Circuit':
copied_circuit = Circuit(device=self._device)
def copy(self) -> 'Circuit':
if self._device == cirq.UNCONSTRAINED_DEVICE:
copied_circuit = Circuit()
else:
copied_circuit = Circuit(device=self._device)
copied_circuit._moments = self._moments[:]
return copied_circuit

def _with_sliced_moments(self, moments: Iterable['cirq.Moment']) -> 'cirq.Circuit':
new_circuit = Circuit(device=self.device)
def _with_sliced_moments(self, moments: Iterable['cirq.Moment']) -> 'Circuit':
if self._device == cirq.UNCONSTRAINED_DEVICE:
new_circuit = Circuit()
else:
new_circuit = Circuit(device=self._device)

new_circuit._moments = list(moments)
return new_circuit

Expand Down Expand Up @@ -1759,8 +1805,8 @@ def __iadd__(self, other):
def __add__(self, other):
if isinstance(other, type(self)):
if (
devices.UNCONSTRAINED_DEVICE not in [self._device, other.device]
and self._device != other.device
devices.UNCONSTRAINED_DEVICE not in [self._device, other._device]
and self._device != other._device
):
raise ValueError("Can't add circuits with incompatible devices.")
elif not isinstance(other, (ops.Operation, Iterable)):
Expand Down Expand Up @@ -1791,6 +1837,8 @@ def __imul__(self, repetitions: INT_TYPE):
def __mul__(self, repetitions: INT_TYPE):
if not isinstance(repetitions, (int, np.integer)):
return NotImplemented
if self._device == cirq.UNCONSTRAINED_DEVICE:
return Circuit(self._moments * int(repetitions))
return Circuit(self._moments * int(repetitions), device=self._device)

def __rmul__(self, repetitions: INT_TYPE):
Expand All @@ -1816,10 +1864,17 @@ def __pow__(self, exponent: int) -> 'cirq.Circuit':
if inv_moment is NotImplemented:
return NotImplemented
inv_moments.append(inv_moment)

if self._device == cirq.UNCONSTRAINED_DEVICE:
return cirq.Circuit(inv_moments)
return cirq.Circuit(inv_moments, device=self._device)

__hash__ = None # type: ignore

@_compat.deprecated(
deadline='v0.15',
fix=_DEVICE_DEP_MESSAGE,
)
def with_device(
self,
new_device: 'cirq.Device',
Expand All @@ -1835,15 +1890,16 @@ def with_device(
Returns:
The translated circuit.
"""
return Circuit(
[
ops.Moment(
operation.transform_qubits(qubit_mapping) for operation in moment.operations
)
for moment in self._moments
],
device=new_device,
)
with _block_overlapping_deprecation():
return Circuit(
[
ops.Moment(
operation.transform_qubits(qubit_mapping) for operation in moment.operations
)
for moment in self._moments
],
device=new_device,
)

def tetris_concat(
*circuits: 'cirq.AbstractCircuit', align: Union['cirq.Alignment', str] = Alignment.LEFT
Expand All @@ -1859,6 +1915,12 @@ def zip(

zip.__doc__ = AbstractCircuit.zip.__doc__

@_compat.deprecated_parameter(
deadline='v0.15',
fix=_DEVICE_DEP_MESSAGE,
parameter_desc='new_device',
match=lambda args, kwargs: 'new_device' in kwargs,
)
def transform_qubits(
self,
qubit_map: Union[Dict['cirq.Qid', 'cirq.Qid'], Callable[['cirq.Qid'], 'cirq.Qid']],
Expand All @@ -1867,10 +1929,6 @@ def transform_qubits(
) -> 'cirq.Circuit':
"""Returns the same circuit, but with different qubits.

Note that this method does essentially the same thing as
`cirq.Circuit.with_device`. It is included regardless because there are
also `transform_qubits` methods on `cirq.Operation` and `cirq.Moment`.

Args:
qubit_map: A function or a dict mapping each current qubit into a desired
new qubit.
Expand All @@ -1891,9 +1949,17 @@ def transform_qubits(
transform = lambda q: qubit_map.get(q, q) # type: ignore
else:
raise TypeError('qubit_map must be a function or dict mapping qubits to qubits.')
return self.with_device(
new_device=self.device if new_device is None else new_device, qubit_mapping=transform
)

op_list = [
ops.Moment(operation.transform_qubits(transform) for operation in moment.operations)
for moment in self._moments
]

if new_device is None and self._device == devices.UNCONSTRAINED_DEVICE:
return Circuit(op_list)

with _block_overlapping_deprecation():
return Circuit(op_list, device=self._device if new_device is None else new_device)

def _prev_moment_available(self, op: 'cirq.Operation', end_moment_index: int) -> Optional[int]:
last_available = end_moment_index
Expand Down Expand Up @@ -2309,8 +2375,10 @@ def _resolve_parameters_(
resolved_operations = _resolve_operations(moment.operations, resolver, recursive)
new_moment = ops.Moment(resolved_operations)
resolved_moments.append(new_moment)
resolved_circuit = Circuit(resolved_moments, device=self.device)
return resolved_circuit
if self._device == devices.UNCONSTRAINED_DEVICE:
return Circuit(resolved_moments)
with _block_overlapping_deprecation():
return Circuit(resolved_moments, device=self._device)

@property
def moments(self):
Expand Down
43 changes: 36 additions & 7 deletions cirq-core/cirq/circuits/circuit_dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import functools
import networkx

from cirq import ops, devices
from cirq import _compat, ops, devices
from cirq.circuits import circuit

if TYPE_CHECKING:
Expand Down Expand Up @@ -70,6 +70,12 @@ class CircuitDag(networkx.DiGraph):

disjoint_qubits = staticmethod(_disjoint_qubits)

@_compat.deprecated_parameter(
deadline='v0.15',
fix=circuit._DEVICE_DEP_MESSAGE,
parameter_desc='device',
match=lambda args, kwargs: 'device' in kwargs or len(args) == 4,
)
def __init__(
self,
can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits,
Expand All @@ -92,28 +98,49 @@ def __init__(
"""
super().__init__(incoming_graph_data)
self.can_reorder = can_reorder
self.device = device
self._device = device

@property # type: ignore
@_compat.deprecated(
deadline='v0.15',
fix=circuit._DEVICE_DEP_MESSAGE,
)
def device(self) -> devices.Device:
return self._device

@staticmethod
def make_node(op: 'cirq.Operation') -> Unique:
return Unique(op)

@staticmethod
def from_circuit(
circuit: 'cirq.Circuit',
circuit: circuit.Circuit,
can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits,
) -> 'CircuitDag':
if circuit._device == devices.UNCONSTRAINED_DEVICE:
return CircuitDag.from_ops(circuit.all_operations(), can_reorder=can_reorder)
return CircuitDag.from_ops(
circuit.all_operations(), can_reorder=can_reorder, device=circuit.device
circuit.all_operations(), can_reorder=can_reorder, device=circuit._device
)

@staticmethod
@_compat.deprecated_parameter(
deadline='v0.15',
fix=circuit._DEVICE_DEP_MESSAGE,
parameter_desc='device',
match=lambda args, kwargs: 'device' in kwargs,
)
def from_ops(
*operations: 'cirq.OP_TREE',
can_reorder: Callable[['cirq.Operation', 'cirq.Operation'], bool] = _disjoint_qubits,
device: 'cirq.Device' = devices.UNCONSTRAINED_DEVICE,
) -> 'CircuitDag':
dag = CircuitDag(can_reorder=can_reorder, device=device)
if device == devices.UNCONSTRAINED_DEVICE:
dag = CircuitDag(can_reorder=can_reorder)
else:
with circuit._block_overlapping_deprecation():
dag = CircuitDag(can_reorder=can_reorder, device=device)

for op in ops.flatten_op_tree(operations):
dag.append(cast(ops.Operation, op))
return dag
Expand Down Expand Up @@ -184,9 +211,11 @@ def all_operations(self) -> Iterator['cirq.Operation']:
def all_qubits(self):
return frozenset(q for node in self.nodes for q in node.val.qubits)

def to_circuit(self) -> 'cirq.Circuit':
def to_circuit(self) -> circuit.Circuit:
if self._device == devices.UNCONSTRAINED_DEVICE:
return circuit.Circuit(self.all_operations(), strategy=circuit.InsertStrategy.EARLIEST)
return circuit.Circuit(
self.all_operations(), strategy=circuit.InsertStrategy.EARLIEST, device=self.device
self.all_operations(), strategy=circuit.InsertStrategy.EARLIEST, device=self._device
)

def findall_nodes_until_blocked(
Expand Down
Loading