Skip to content

Commit

Permalink
Deprecate ciruit.device everywhere. (quantumlib#4845)
Browse files Browse the repository at this point in the history
Last item in step 1 of quantumlib#4744 .
  • Loading branch information
MichaelBroughton committed Jan 22, 2022
1 parent 88332fc commit cc182ee
Show file tree
Hide file tree
Showing 9 changed files with 399 additions and 93 deletions.
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)

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

0 comments on commit cc182ee

Please sign in to comment.