Skip to content

Commit 38385b5

Browse files
authored
Merge branch 'master' into core-np1.24
2 parents 2e579ee + 8b97aa5 commit 38385b5

37 files changed

+515
-120
lines changed

.github/workflows/ci.yml

+4
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ jobs:
336336
runs-on: ubuntu-20.04
337337
steps:
338338
- uses: actions/checkout@v2
339+
- uses: actions/setup-node@v2
339340
with:
340341
node-version: '14.16.1'
341342
- name: Install npm
@@ -347,6 +348,7 @@ jobs:
347348
runs-on: ubuntu-20.04
348349
steps:
349350
- uses: actions/checkout@v2
351+
- uses: actions/setup-node@v2
350352
with:
351353
node-version: '14.16.1'
352354
- name: Install npm
@@ -358,6 +360,7 @@ jobs:
358360
runs-on: ubuntu-20.04
359361
steps:
360362
- uses: actions/checkout@v2
363+
- uses: actions/setup-node@v2
361364
with:
362365
node-version: '14.16.1'
363366
- name: Install npm dependencies
@@ -371,6 +374,7 @@ jobs:
371374
runs-on: ubuntu-20.04
372375
steps:
373376
- uses: actions/checkout@v2
377+
- uses: actions/setup-node@v2
374378
with:
375379
node-version: '14.16.1'
376380
- name: Install npm dependencies

asv.conf.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"environment_type": "virtualenv",
1010
"show_commit_url": "https://github.com/quantumlib/Cirq/commit/",
1111
"pythons": ["3.8"],
12+
"matrix": {"env_nobuild": {"PYTHONOPTIMIZE": ["-O", ""]}},
1213
"benchmark_dir": "benchmarks",
1314
"env_dir": ".asv/env",
1415
"results_dir": ".asv/results",

cirq-core/cirq/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
from cirq import _import
1818

19+
from cirq._compat import __cirq_debug__, with_debug
20+
1921
# A module can only depend on modules imported earlier in this list of modules
2022
# at import time. Pytest will fail otherwise (enforced by
2123
# dev_tools/import_test.py).
@@ -236,6 +238,7 @@
236238
LinearCombinationOfOperations,
237239
MatrixGate,
238240
MixedUnitaryChannel,
241+
M,
239242
measure,
240243
measure_each,
241244
measure_paulistring_terms,
@@ -284,6 +287,7 @@
284287
QubitOrder,
285288
QubitOrderOrList,
286289
QubitPermutationGate,
290+
R,
287291
reset,
288292
reset_each,
289293
ResetChannel,

cirq-core/cirq/_compat.py

+28-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
"""Workarounds for compatibility issues between versions and libraries."""
1616
import contextlib
17+
import contextvars
1718
import dataclasses
1819
import functools
1920
import importlib
@@ -24,15 +25,41 @@
2425
import traceback
2526
import warnings
2627
from types import ModuleType
27-
from typing import Any, Callable, Dict, Optional, overload, Set, Tuple, Type, TypeVar
28+
from typing import Any, Callable, Dict, Iterator, Optional, overload, Set, Tuple, Type, TypeVar
2829

2930
import numpy as np
3031
import pandas as pd
3132
import sympy
3233
import sympy.printing.repr
3334

35+
from cirq._doc import document
36+
3437
ALLOW_DEPRECATION_IN_TEST = 'ALLOW_DEPRECATION_IN_TEST'
3538

39+
__cirq_debug__ = contextvars.ContextVar('__cirq_debug__', default=__debug__)
40+
document(
41+
__cirq_debug__,
42+
"A cirq specific flag which can be used to conditionally turn off all validations across Cirq "
43+
"to boost performance in production mode. Defaults to python's built-in constant __debug__. "
44+
"The flag is implemented as a `ContextVar` and is thread safe.",
45+
)
46+
47+
48+
@contextlib.contextmanager
49+
def with_debug(value: bool) -> Iterator[None]:
50+
"""Sets the value of global constant `cirq.__cirq_debug__` within the context.
51+
52+
If `__cirq_debug__` is set to False, all validations in Cirq are disabled to optimize
53+
performance. Users should use the `cirq.with_debug` context manager instead of manually
54+
mutating the value of `__cirq_debug__` flag. On exit, the context manager resets the
55+
value of `__cirq_debug__` flag to what it was before entering the context manager.
56+
"""
57+
token = __cirq_debug__.set(value)
58+
try:
59+
yield
60+
finally:
61+
__cirq_debug__.reset(token)
62+
3663

3764
try:
3865
from functools import cached_property # pylint: disable=unused-import

cirq-core/cirq/_compat_test.py

+10
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@
5151
)
5252

5353

54+
def test_with_debug():
55+
assert cirq.__cirq_debug__.get()
56+
with cirq.with_debug(False):
57+
assert not cirq.__cirq_debug__.get()
58+
with cirq.with_debug(True):
59+
assert cirq.__cirq_debug__.get()
60+
assert not cirq.__cirq_debug__.get()
61+
assert cirq.__cirq_debug__.get()
62+
63+
5464
def test_proper_repr():
5565
v = sympy.Symbol('t') * 3
5666
v2 = eval(proper_repr(v))

cirq-core/cirq/circuits/circuit.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -1861,9 +1861,8 @@ def __setitem__(self, key: slice, value: Iterable['cirq.Moment']):
18611861
pass
18621862

18631863
def __setitem__(self, key, value):
1864-
if isinstance(key, int):
1865-
if not isinstance(value, Moment):
1866-
raise TypeError('Can only assign Moments into Circuits.')
1864+
if isinstance(key, int) and not isinstance(value, Moment):
1865+
raise TypeError('Can only assign Moments into Circuits.')
18671866

18681867
if isinstance(key, slice):
18691868
value = list(value)
@@ -2595,8 +2594,7 @@ def _get_global_phase_and_tags_for_op(op: 'cirq.Operation') -> Tuple[Optional[co
25952594
elif isinstance(op.untagged, CircuitOperation):
25962595
op_phase, op_tags = _get_global_phase_and_tags_for_ops(op.untagged.circuit.all_operations())
25972596
return op_phase, list(op.tags) + op_tags
2598-
else:
2599-
return None, []
2597+
return None, []
26002598

26012599

26022600
def _get_global_phase_and_tags_for_ops(op_list: Any) -> Tuple[Optional[complex], List[Any]]:

cirq-core/cirq/circuits/frozen_circuit.py

+13-1
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
"""An immutable version of the Circuit data structure."""
15-
from typing import FrozenSet, Iterable, Iterator, Sequence, Tuple, TYPE_CHECKING, Union
15+
from typing import AbstractSet, FrozenSet, Iterable, Iterator, Sequence, Tuple, TYPE_CHECKING, Union
1616

1717
import numpy as np
1818

@@ -83,6 +83,10 @@ def _num_qubits_(self) -> int:
8383
def _qid_shape_(self) -> Tuple[int, ...]:
8484
return super()._qid_shape_()
8585

86+
@_compat.cached_method
87+
def _has_unitary_(self) -> bool:
88+
return super()._has_unitary_()
89+
8690
@_compat.cached_method
8791
def _unitary_(self) -> Union[np.ndarray, NotImplementedType]:
8892
return super()._unitary_()
@@ -124,6 +128,14 @@ def are_all_measurements_terminal(self) -> bool:
124128
def all_measurement_key_names(self) -> FrozenSet[str]:
125129
return frozenset(str(key) for key in self.all_measurement_key_objs())
126130

131+
@_compat.cached_method
132+
def _is_parameterized_(self) -> bool:
133+
return super()._is_parameterized_()
134+
135+
@_compat.cached_method
136+
def _parameter_names_(self) -> AbstractSet[str]:
137+
return super()._parameter_names_()
138+
127139
def _measurement_key_names_(self) -> FrozenSet[str]:
128140
return self.all_measurement_key_names()
129141

cirq-core/cirq/circuits/moment.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
import numpy as np
3838

39-
from cirq import protocols, ops, qis
39+
from cirq import protocols, ops, qis, _compat
4040
from cirq._import import LazyLoader
4141
from cirq.ops import raw_types, op_tree
4242
from cirq.protocols import circuit_diagram_info_protocol
@@ -148,8 +148,7 @@ def operation_at(self, qubit: raw_types.Qid) -> Optional['cirq.Operation']:
148148
"""
149149
if self.operates_on([qubit]):
150150
return self.__getitem__(qubit)
151-
else:
152-
return None
151+
return None
153152

154153
def with_operation(self, operation: 'cirq.Operation') -> 'cirq.Moment':
155154
"""Returns an equal moment, but with the given op added.
@@ -238,9 +237,11 @@ def without_operations_touching(self, qubits: Iterable['cirq.Qid']) -> 'cirq.Mom
238237
if qubits.isdisjoint(frozenset(operation.qubits))
239238
)
240239

240+
@_compat.cached_method()
241241
def _is_parameterized_(self) -> bool:
242242
return any(protocols.is_parameterized(op) for op in self)
243243

244+
@_compat.cached_method()
244245
def _parameter_names_(self) -> AbstractSet[str]:
245246
return {name for op in self for name in protocols.parameter_names(op)}
246247

@@ -266,6 +267,7 @@ def _with_measurement_key_mapping_(self, key_map: Mapping[str, str]):
266267
for op in self.operations
267268
)
268269

270+
@_compat.cached_method()
269271
def _measurement_key_names_(self) -> FrozenSet[str]:
270272
return frozenset(str(key) for key in self._measurement_key_objs_())
271273

@@ -333,6 +335,7 @@ def _approx_eq_(self, other: Any, atol: Union[int, float]) -> bool:
333335
def __ne__(self, other) -> bool:
334336
return not self == other
335337

338+
@_compat.cached_method()
336339
def __hash__(self):
337340
return hash((Moment, self._sorted_operations_()))
338341

@@ -406,6 +409,7 @@ def expand_to(self, qubits: Iterable['cirq.Qid']) -> 'cirq.Moment':
406409
operations.append(ops.I(q))
407410
return Moment(*operations)
408411

412+
@_compat.cached_method()
409413
def _has_kraus_(self) -> bool:
410414
"""Returns True if self has a Kraus representation and self uses <= 10 qubits."""
411415
return all(protocols.has_kraus(op) for op in self.operations) and len(self.qubits) <= 10

cirq-core/cirq/ops/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
phase_flip,
4242
PhaseDampingChannel,
4343
PhaseFlipChannel,
44+
R,
4445
reset,
4546
reset_each,
4647
ResetChannel,
@@ -126,6 +127,7 @@
126127
from cirq.ops.matrix_gates import MatrixGate
127128

128129
from cirq.ops.measure_util import (
130+
M,
129131
measure,
130132
measure_each,
131133
measure_paulistring_terms,

cirq-core/cirq/ops/common_channels.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -775,10 +775,16 @@ def _json_dict_(self) -> Dict[str, Any]:
775775

776776

777777
def reset(qubit: 'cirq.Qid') -> raw_types.Operation:
778-
"""Returns a `cirq.ResetChannel` on the given qubit."""
778+
"""Returns a `cirq.ResetChannel` on the given qubit.
779+
780+
This can also be used with the alias `cirq.R`.
781+
"""
779782
return ResetChannel(qubit.dimension).on(qubit)
780783

781784

785+
R = reset
786+
787+
782788
def reset_each(*qubits: 'cirq.Qid') -> List[raw_types.Operation]:
783789
"""Returns a list of `cirq.ResetChannel` instances on the given qubits."""
784790
return [ResetChannel(q.dimension).on(q) for q in qubits]

cirq-core/cirq/ops/common_gates.py

+32
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def __init__(
120120
super().__init__(exponent=exponent, global_shift=global_shift)
121121
self._dimension = dimension
122122

123+
@property
124+
def dimension(self) -> value.TParamVal:
125+
return self._dimension
126+
123127
def _num_qubits_(self) -> int:
124128
return 1
125129

@@ -316,6 +320,18 @@ def __repr__(self) -> str:
316320
all_args = ', '.join(args)
317321
return f'cirq.XPowGate({all_args})'
318322

323+
def _json_dict_(self) -> Dict[str, Any]:
324+
d = protocols.obj_to_dict_helper(self, ['exponent', 'global_shift'])
325+
if self.dimension != 2:
326+
d['dimension'] = self.dimension
327+
return d
328+
329+
def _value_equality_values_(self):
330+
return (*super()._value_equality_values_(), self._dimension)
331+
332+
def _value_equality_approximate_values_(self):
333+
return (*super()._value_equality_approximate_values_(), self._dimension)
334+
319335

320336
class Rx(XPowGate):
321337
r"""A gate with matrix $e^{-i X t/2}$ that rotates around the X axis of the Bloch sphere by $t$.
@@ -608,6 +624,10 @@ def __init__(
608624
super().__init__(exponent=exponent, global_shift=global_shift)
609625
self._dimension = dimension
610626

627+
@property
628+
def dimension(self) -> value.TParamVal:
629+
return self._dimension
630+
611631
def _num_qubits_(self) -> int:
612632
return 1
613633

@@ -834,6 +854,18 @@ def _commutes_on_qids_(
834854
return NotImplemented
835855
return True
836856

857+
def _json_dict_(self) -> Dict[str, Any]:
858+
d = protocols.obj_to_dict_helper(self, ['exponent', 'global_shift'])
859+
if self.dimension != 2:
860+
d['dimension'] = self.dimension
861+
return d
862+
863+
def _value_equality_values_(self):
864+
return (*super()._value_equality_values_(), self._dimension)
865+
866+
def _value_equality_approximate_values_(self):
867+
return (*super()._value_equality_approximate_values_(), self._dimension)
868+
837869

838870
class Rz(ZPowGate):
839871
r"""A gate with matrix $e^{-i Z t/2}$ that rotates around the Z axis of the Bloch sphere by $t$.

cirq-core/cirq/ops/common_gates_test.py

+4
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,7 @@ def test_approx_eq():
11001100

11011101
def test_xpow_dim_3():
11021102
x = cirq.XPowGate(dimension=3)
1103+
assert cirq.X != x
11031104
# fmt: off
11041105
expected = [
11051106
[0, 0, 1],
@@ -1127,6 +1128,7 @@ def test_xpow_dim_3():
11271128

11281129
def test_xpow_dim_4():
11291130
x = cirq.XPowGate(dimension=4)
1131+
assert cirq.X != x
11301132
# fmt: off
11311133
expected = [
11321134
[0, 0, 0, 1],
@@ -1159,6 +1161,7 @@ def test_zpow_dim_3():
11591161
L = np.exp(2 * np.pi * 1j / 3)
11601162
L2 = L**2
11611163
z = cirq.ZPowGate(dimension=3)
1164+
assert cirq.Z != z
11621165
# fmt: off
11631166
expected = [
11641167
[1, 0, 0],
@@ -1209,6 +1212,7 @@ def test_zpow_dim_3():
12091212

12101213
def test_zpow_dim_4():
12111214
z = cirq.ZPowGate(dimension=4)
1215+
assert cirq.Z != z
12121216
# fmt: off
12131217
expected = [
12141218
[1, 0, 0, 0],

cirq-core/cirq/ops/matrix_gates.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,15 @@ def with_name(self, name: str) -> 'MatrixGate':
114114
return MatrixGate(self._matrix, name=name, qid_shape=self._qid_shape, unitary_check=False)
115115

116116
def _json_dict_(self) -> Dict[str, Any]:
117-
return {'matrix': self._matrix.tolist(), 'qid_shape': self._qid_shape}
117+
return {
118+
'matrix': self._matrix.tolist(),
119+
'qid_shape': self._qid_shape,
120+
**({'name': self._name} if self._name is not None else {}),
121+
}
118122

119123
@classmethod
120-
def _from_json_dict_(cls, matrix, qid_shape, **kwargs):
121-
return cls(matrix=np.array(matrix), qid_shape=qid_shape)
124+
def _from_json_dict_(cls, matrix, qid_shape, name=None, **kwargs):
125+
return cls(matrix=np.array(matrix), qid_shape=qid_shape, name=name)
122126

123127
def _qid_shape_(self) -> Tuple[int, ...]:
124128
return self._qid_shape

0 commit comments

Comments
 (0)