cirq-core 1.3.0.dev20231201141002__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cirq-core might be problematic. Click here for more details.

Files changed (157) hide show
  1. cirq/__init__.py +4 -0
  2. cirq/_compat.py +9 -11
  3. cirq/_compat_test.py +45 -56
  4. cirq/_version.py +31 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/circuit.py +13 -8
  7. cirq/circuits/circuit_operation.py +2 -1
  8. cirq/circuits/circuit_test.py +2 -2
  9. cirq/circuits/frozen_circuit.py +3 -2
  10. cirq/circuits/moment.py +12 -10
  11. cirq/circuits/qasm_output.py +5 -1
  12. cirq/circuits/qasm_output_test.py +25 -10
  13. cirq/contrib/qcircuit/qcircuit_diagram_info.py +9 -7
  14. cirq/contrib/quimb/mps_simulator_test.py +1 -1
  15. cirq/contrib/quimb/state_vector.py +9 -2
  16. cirq/contrib/svg/svg.py +2 -1
  17. cirq/contrib/svg/svg_test.py +1 -0
  18. cirq/devices/grid_qubit.py +85 -32
  19. cirq/devices/grid_qubit_test.py +22 -4
  20. cirq/devices/line_qubit.py +74 -26
  21. cirq/devices/line_qubit_test.py +19 -0
  22. cirq/devices/noise_utils.py +33 -31
  23. cirq/devices/noise_utils_test.py +1 -84
  24. cirq/devices/superconducting_qubits_noise_properties.py +7 -6
  25. cirq/experiments/__init__.py +8 -0
  26. cirq/experiments/qubit_characterizations.py +288 -44
  27. cirq/experiments/qubit_characterizations_test.py +61 -7
  28. cirq/experiments/random_quantum_circuit_generation.py +1 -1
  29. cirq/experiments/single_qubit_readout_calibration.py +132 -6
  30. cirq/experiments/single_qubit_readout_calibration_test.py +3 -1
  31. cirq/experiments/t1_decay_experiment.py +14 -7
  32. cirq/experiments/t1_decay_experiment_test.py +14 -26
  33. cirq/experiments/two_qubit_xeb.py +483 -0
  34. cirq/experiments/two_qubit_xeb_test.py +304 -0
  35. cirq/json_resolver_cache.py +2 -0
  36. cirq/linalg/decompositions.py +11 -13
  37. cirq/linalg/decompositions_test.py +1 -3
  38. cirq/linalg/diagonalize.py +5 -4
  39. cirq/linalg/predicates.py +8 -6
  40. cirq/linalg/transformations.py +2 -1
  41. cirq/linalg/transformations_test.py +1 -1
  42. cirq/ops/__init__.py +2 -0
  43. cirq/ops/clifford_gate.py +59 -16
  44. cirq/ops/common_gates_test.py +1 -2
  45. cirq/ops/control_values.py +4 -3
  46. cirq/ops/controlled_gate_test.py +1 -3
  47. cirq/ops/gate_operation.py +10 -1
  48. cirq/ops/named_qubit.py +74 -28
  49. cirq/ops/named_qubit_test.py +19 -0
  50. cirq/ops/parity_gates.py +5 -0
  51. cirq/ops/parity_gates_test.py +2 -10
  52. cirq/ops/pauli_gates.py +5 -2
  53. cirq/ops/pauli_string.py +2 -2
  54. cirq/ops/permutation_gate.py +16 -18
  55. cirq/ops/phased_iswap_gate_test.py +1 -3
  56. cirq/ops/phased_x_gate.py +1 -1
  57. cirq/ops/phased_x_z_gate.py +17 -1
  58. cirq/ops/phased_x_z_gate_test.py +24 -0
  59. cirq/ops/qid_util.py +4 -8
  60. cirq/ops/qubit_manager.py +7 -4
  61. cirq/ops/qubit_manager_test.py +20 -0
  62. cirq/ops/raw_types.py +5 -2
  63. cirq/ops/raw_types_test.py +14 -15
  64. cirq/ops/uniform_superposition_gate.py +123 -0
  65. cirq/ops/uniform_superposition_gate_test.py +94 -0
  66. cirq/protocols/approximate_equality_protocol_test.py +2 -2
  67. cirq/protocols/circuit_diagram_info_protocol.py +6 -4
  68. cirq/protocols/commutes_protocol.py +2 -4
  69. cirq/protocols/decompose_protocol.py +7 -12
  70. cirq/protocols/decompose_protocol_test.py +7 -3
  71. cirq/protocols/has_stabilizer_effect_protocol.py +1 -5
  72. cirq/protocols/has_stabilizer_effect_protocol_test.py +13 -4
  73. cirq/protocols/json_serialization.py +51 -181
  74. cirq/protocols/json_serialization_test.py +13 -47
  75. cirq/protocols/json_test_data/CircuitOperation.json +131 -148
  76. cirq/protocols/json_test_data/CircuitOperation.json_inward +55 -0
  77. cirq/protocols/json_test_data/CircuitOperation.repr_inward +6 -0
  78. cirq/protocols/json_test_data/FrozenCircuit.json +196 -210
  79. cirq/protocols/json_test_data/FrozenCircuit.json_inward +35 -0
  80. cirq/protocols/json_test_data/FrozenCircuit.repr_inward +4 -0
  81. cirq/protocols/json_test_data/UniformSuperpositionGate.json +5 -0
  82. cirq/protocols/json_test_data/UniformSuperpositionGate.repr +1 -0
  83. cirq/protocols/json_test_data/cirq.MSGate.json +4 -0
  84. cirq/protocols/json_test_data/cirq.MSGate.repr +1 -0
  85. cirq/protocols/json_test_data/spec.py +2 -0
  86. cirq/protocols/pow_protocol_test.py +1 -3
  87. cirq/protocols/resolve_parameters.py +4 -2
  88. cirq/qis/__init__.py +10 -0
  89. cirq/qis/clifford_tableau.py +8 -2
  90. cirq/qis/noise_utils.py +123 -0
  91. cirq/qis/noise_utils_test.py +97 -0
  92. cirq/sim/classical_simulator.py +227 -87
  93. cirq/sim/classical_simulator_test.py +135 -0
  94. cirq/sim/clifford/clifford_simulator_test.py +4 -2
  95. cirq/sim/mux.py +5 -3
  96. cirq/sim/simulation_product_state.py +15 -10
  97. cirq/sim/simulation_state.py +1 -1
  98. cirq/sim/simulation_state_test.py +2 -2
  99. cirq/sim/simulator_base.py +3 -3
  100. cirq/sim/state_vector_simulation_state.py +4 -4
  101. cirq/sim/state_vector_simulator.py +17 -2
  102. cirq/study/__init__.py +1 -0
  103. cirq/study/result.py +14 -0
  104. cirq/study/result_test.py +6 -0
  105. cirq/study/sweeps.py +4 -2
  106. cirq/study/sweeps_test.py +8 -0
  107. cirq/testing/__init__.py +6 -1
  108. cirq/testing/_compat_test_data/__init__.py +3 -3
  109. cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
  110. cirq/testing/circuit_compare.py +1 -1
  111. cirq/testing/consistent_qasm.py +6 -0
  112. cirq/testing/gate_features.py +10 -0
  113. cirq/testing/lin_alg_utils.py +5 -3
  114. cirq/transformers/__init__.py +15 -0
  115. cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +3 -1
  116. cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +24 -0
  117. cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +17 -0
  118. cirq/transformers/dynamical_decoupling.py +122 -0
  119. cirq/transformers/dynamical_decoupling_test.py +123 -0
  120. cirq/transformers/gauge_compiling/__init__.py +26 -0
  121. cirq/transformers/gauge_compiling/cz_gauge.py +46 -0
  122. cirq/transformers/gauge_compiling/cz_gauge_test.py +23 -0
  123. cirq/transformers/gauge_compiling/gauge_compiling.py +214 -0
  124. cirq/transformers/gauge_compiling/gauge_compiling_test.py +41 -0
  125. cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +83 -0
  126. cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +52 -0
  127. cirq/transformers/gauge_compiling/iswap_gauge.py +105 -0
  128. cirq/transformers/gauge_compiling/iswap_gauge_test.py +23 -0
  129. cirq/transformers/gauge_compiling/spin_inversion_gauge.py +33 -0
  130. cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +37 -0
  131. cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +64 -0
  132. cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +27 -0
  133. cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +94 -0
  134. cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +22 -0
  135. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -0
  136. cirq/transformers/merge_k_qubit_gates_test.py +23 -23
  137. cirq/transformers/merge_single_qubit_gates_test.py +14 -14
  138. cirq/transformers/optimize_for_target_gateset.py +39 -17
  139. cirq/transformers/optimize_for_target_gateset_test.py +189 -39
  140. cirq/transformers/qubit_management_transformers.py +1 -1
  141. cirq/transformers/routing/visualize_routed_circuit_test.py +17 -17
  142. cirq/transformers/stratify_test.py +13 -13
  143. cirq/transformers/target_gatesets/compilation_target_gateset.py +26 -2
  144. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +16 -16
  145. cirq/transformers/target_gatesets/cz_gateset.py +4 -0
  146. cirq/transformers/transformer_api.py +1 -2
  147. cirq/transformers/transformer_primitives.py +15 -14
  148. cirq/transformers/transformer_primitives_test.py +99 -72
  149. cirq/value/classical_data.py +6 -6
  150. cirq/value/value_equality_attr.py +4 -0
  151. cirq/work/sampler.py +3 -4
  152. cirq/work/sampler_test.py +25 -0
  153. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
  154. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
  155. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
  156. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
  157. {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
@@ -15,6 +15,7 @@
15
15
  """Basic types defining qubits, gates, and operations."""
16
16
 
17
17
  import re
18
+ import warnings
18
19
  from typing import (
19
20
  AbstractSet,
20
21
  Any,
@@ -35,7 +36,7 @@ from typing_extensions import Self
35
36
 
36
37
  import numpy as np
37
38
 
38
- from cirq import protocols, value
39
+ from cirq import ops, protocols, value
39
40
  from cirq.ops import raw_types, gate_features, control_values as cv
40
41
  from cirq.type_workarounds import NotImplementedType
41
42
 
@@ -348,6 +349,14 @@ class GateOperation(raw_types.Operation):
348
349
  return self.gate._rmul_with_qubits(self._qubits, other)
349
350
 
350
351
  def _qasm_(self, args: 'protocols.QasmArgs') -> Optional[str]:
352
+ if isinstance(self.gate, ops.GlobalPhaseGate):
353
+ warnings.warn(
354
+ "OpenQASM 2.0 does not support global phase."
355
+ "Since the global phase does not affect the measurement results, "
356
+ "the conversion to QASM is disregarded."
357
+ )
358
+ return ""
359
+
351
360
  return protocols.qasm(self.gate, args=args, qubits=self.qubits, default=None)
352
361
 
353
362
  def _equal_up_to_global_phase_(
cirq/ops/named_qubit.py CHANGED
@@ -12,7 +12,8 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  import functools
15
- from typing import Any, Dict, List, Optional, TYPE_CHECKING
15
+ import weakref
16
+ from typing import Any, Dict, List, Optional, Tuple, TYPE_CHECKING
16
17
 
17
18
  from cirq import protocols
18
19
  from cirq.ops import raw_types
@@ -31,34 +32,55 @@ class _BaseNamedQid(raw_types.Qid):
31
32
  _comp_key: Optional[str] = None
32
33
  _hash: Optional[int] = None
33
34
 
34
- def __getstate__(self):
35
- # Don't save hash when pickling; see #3777.
36
- state = self.__dict__
37
- if "_hash" in state or "_comp_key" in state:
38
- state = state.copy()
39
- if "_hash" in state:
40
- del state["_hash"]
41
- if "_comp_key" in state:
42
- del state["_comp_key"]
43
- return state
44
-
45
35
  def __hash__(self) -> int:
46
36
  if self._hash is None:
47
37
  self._hash = hash((self._name, self._dimension))
48
38
  return self._hash
49
39
 
50
- def __eq__(self, other):
40
+ def __eq__(self, other) -> bool:
51
41
  # Explicitly implemented for performance (vs delegating to Qid).
52
42
  if isinstance(other, _BaseNamedQid):
53
- return self._name == other._name and self._dimension == other._dimension
43
+ return self is other or (
44
+ self._name == other._name and self._dimension == other._dimension
45
+ )
54
46
  return NotImplemented
55
47
 
56
- def __ne__(self, other):
48
+ def __ne__(self, other) -> bool:
57
49
  # Explicitly implemented for performance (vs delegating to Qid).
58
50
  if isinstance(other, _BaseNamedQid):
59
- return self._name != other._name or self._dimension != other._dimension
51
+ return self is not other and (
52
+ self._name != other._name or self._dimension != other._dimension
53
+ )
60
54
  return NotImplemented
61
55
 
56
+ def __lt__(self, other) -> bool:
57
+ # Explicitly implemented for performance (vs delegating to Qid).
58
+ if isinstance(other, _BaseNamedQid):
59
+ k0, k1 = self._comparison_key(), other._comparison_key()
60
+ return k0 < k1 or (k0 == k1 and self._dimension < other._dimension)
61
+ return super().__lt__(other)
62
+
63
+ def __le__(self, other) -> bool:
64
+ # Explicitly implemented for performance (vs delegating to Qid).
65
+ if isinstance(other, _BaseNamedQid):
66
+ k0, k1 = self._comparison_key(), other._comparison_key()
67
+ return k0 < k1 or (k0 == k1 and self._dimension <= other._dimension)
68
+ return super().__le__(other)
69
+
70
+ def __ge__(self, other) -> bool:
71
+ # Explicitly implemented for performance (vs delegating to Qid).
72
+ if isinstance(other, _BaseNamedQid):
73
+ k0, k1 = self._comparison_key(), other._comparison_key()
74
+ return k0 > k1 or (k0 == k1 and self._dimension >= other._dimension)
75
+ return super().__ge__(other)
76
+
77
+ def __gt__(self, other) -> bool:
78
+ # Explicitly implemented for performance (vs delegating to Qid).
79
+ if isinstance(other, _BaseNamedQid):
80
+ k0, k1 = self._comparison_key(), other._comparison_key()
81
+ return k0 > k1 or (k0 == k1 and self._dimension > other._dimension)
82
+ return super().__gt__(other)
83
+
62
84
  def _comparison_key(self):
63
85
  if self._comp_key is None:
64
86
  self._comp_key = _pad_digits(self._name)
@@ -86,7 +108,11 @@ class NamedQid(_BaseNamedQid):
86
108
  correctly come before 'qid22'.
87
109
  """
88
110
 
89
- def __init__(self, name: str, dimension: int) -> None:
111
+ # Cache of existing NamedQid instances, returned by __new__ if available.
112
+ # Holds weak references so instances can still be garbage collected.
113
+ _cache = weakref.WeakValueDictionary[Tuple[str, int], 'cirq.NamedQid']()
114
+
115
+ def __new__(cls, name: str, dimension: int) -> 'cirq.NamedQid':
90
116
  """Initializes a `NamedQid` with a given name and dimension.
91
117
 
92
118
  Args:
@@ -94,9 +120,19 @@ class NamedQid(_BaseNamedQid):
94
120
  dimension: The dimension of the qid's Hilbert space, i.e.
95
121
  the number of quantum levels.
96
122
  """
97
- self.validate_dimension(dimension)
98
- self._name = name
99
- self._dimension = dimension
123
+ key = (name, dimension)
124
+ inst = cls._cache.get(key)
125
+ if inst is None:
126
+ cls.validate_dimension(dimension)
127
+ inst = super().__new__(cls)
128
+ inst._name = name
129
+ inst._dimension = dimension
130
+ cls._cache[key] = inst
131
+ return inst
132
+
133
+ def __getnewargs__(self):
134
+ """Returns a tuple of args to pass to __new__ when unpickling."""
135
+ return (self._name, self._dimension)
100
136
 
101
137
  def __repr__(self) -> str:
102
138
  return f'cirq.NamedQid({self._name!r}, dimension={self._dimension})'
@@ -143,18 +179,28 @@ class NamedQubit(_BaseNamedQid):
143
179
 
144
180
  _dimension = 2
145
181
 
146
- def __init__(self, name: str) -> None:
147
- """Initializes a `NamedQubit` with a given name.
182
+ # Cache of existing NamedQubit instances, returned by __new__ if available.
183
+ # Holds weak references so instances can still be garbage collected.
184
+ _cache = weakref.WeakValueDictionary[str, 'cirq.NamedQubit']()
185
+
186
+ def __new__(cls, name: str) -> 'cirq.NamedQubit':
187
+ """Initializes a `NamedQid` with a given name and dimension.
148
188
 
149
189
  Args:
150
190
  name: The name.
191
+ dimension: The dimension of the qid's Hilbert space, i.e.
192
+ the number of quantum levels.
151
193
  """
152
- self._name = name
153
-
154
- def _cmp_tuple(self):
155
- cls = NamedQid if type(self) is NamedQubit else type(self)
156
- # Must be same as Qid._cmp_tuple but with cls in place of type(self).
157
- return (cls.__name__, repr(cls), self._comparison_key(), self._dimension)
194
+ inst = cls._cache.get(name)
195
+ if inst is None:
196
+ inst = super().__new__(cls)
197
+ inst._name = name
198
+ cls._cache[name] = inst
199
+ return inst
200
+
201
+ def __getnewargs__(self):
202
+ """Returns a tuple of args to pass to __new__ when unpickling."""
203
+ return (self._name,)
158
204
 
159
205
  def __str__(self) -> str:
160
206
  return self._name
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import cirq
16
+ from cirq.devices.grid_qubit_test import _test_qid_pickled_hash
16
17
  from cirq.ops.named_qubit import _pad_digits
17
18
 
18
19
 
@@ -41,6 +42,24 @@ def test_named_qubit_repr():
41
42
  assert repr(qid) == "cirq.NamedQid('a', dimension=3)"
42
43
 
43
44
 
45
+ def test_named_qubit_pickled_hash():
46
+ # Use a name that is unlikely to be used by any other tests.
47
+ x = "test_named_qubit_pickled_hash"
48
+ q_bad = cirq.NamedQubit(x)
49
+ cirq.NamedQubit._cache.pop(x)
50
+ q = cirq.NamedQubit(x)
51
+ _test_qid_pickled_hash(q, q_bad)
52
+
53
+
54
+ def test_named_qid_pickled_hash():
55
+ # Use a name that is unlikely to be used by any other tests.
56
+ x = "test_named_qid_pickled_hash"
57
+ q_bad = cirq.NamedQid(x, dimension=3)
58
+ cirq.NamedQid._cache.pop((x, 3))
59
+ q = cirq.NamedQid(x, dimension=3)
60
+ _test_qid_pickled_hash(q, q_bad)
61
+
62
+
44
63
  def test_named_qubit_order():
45
64
  order = cirq.testing.OrderTester()
46
65
  order.add_ascending(
cirq/ops/parity_gates.py CHANGED
@@ -399,6 +399,11 @@ class MSGate(XXPowGate):
399
399
  return 'cirq.ms(np.pi/2)'
400
400
  return f'cirq.ms({self._exponent!r}*np.pi/2)'
401
401
 
402
+ # the default namespace is already occupied by cirq_ionq.MSGate
403
+ @classmethod
404
+ def _json_namespace_(cls) -> str:
405
+ return 'cirq'
406
+
402
407
  def _json_dict_(self) -> Dict[str, Any]:
403
408
  return protocols.obj_to_dict_helper(self, ["rads"])
404
409
 
@@ -258,7 +258,7 @@ def test_trace_distance():
258
258
  def test_ms_arguments():
259
259
  eq_tester = cirq.testing.EqualsTester()
260
260
  eq_tester.add_equality_group(
261
- cirq.ms(np.pi / 2), cirq.ops.MSGate(rads=np.pi / 2), cirq.XXPowGate(global_shift=-0.5)
261
+ cirq.ms(np.pi / 2), cirq.MSGate(rads=np.pi / 2), cirq.XXPowGate(global_shift=-0.5)
262
262
  )
263
263
  eq_tester.add_equality_group(
264
264
  cirq.ms(np.pi / 4), cirq.XXPowGate(exponent=0.5, global_shift=-0.5)
@@ -323,15 +323,7 @@ b: ───×───────────MS(π)───
323
323
 
324
324
 
325
325
  def test_json_serialization():
326
- def custom_resolver(cirq_type: str):
327
- if cirq_type == "MSGate":
328
- return cirq.ops.MSGate
329
- return None
330
-
331
- assert cirq.read_json(
332
- json_text=cirq.to_json(cirq.ms(np.pi / 2)), resolvers=[custom_resolver]
333
- ) == cirq.ms(np.pi / 2)
334
- assert custom_resolver('X') is None
326
+ assert cirq.read_json(json_text=cirq.to_json(cirq.ms(np.pi / 2))) == cirq.ms(np.pi / 2)
335
327
 
336
328
 
337
329
  @pytest.mark.parametrize('gate_cls', (cirq.XXPowGate, cirq.YYPowGate, cirq.ZZPowGate))
cirq/ops/pauli_gates.py CHANGED
@@ -15,6 +15,7 @@ import abc
15
15
  from typing import Any, cast, Tuple, TYPE_CHECKING, Union, Dict
16
16
 
17
17
  from cirq._doc import document
18
+ from cirq._import import LazyLoader
18
19
  from cirq.ops import common_gates, raw_types, identity
19
20
  from cirq.type_workarounds import NotImplementedType
20
21
 
@@ -29,6 +30,9 @@ if TYPE_CHECKING:
29
30
  ) # pragma: no cover
30
31
 
31
32
 
33
+ pauli_string = LazyLoader("pauli_string", globals(), "cirq.ops.pauli_string")
34
+
35
+
32
36
  class Pauli(raw_types.Gate, metaclass=abc.ABCMeta):
33
37
  """Represents the Pauli gates.
34
38
 
@@ -97,9 +101,8 @@ class Pauli(raw_types.Gate, metaclass=abc.ABCMeta):
97
101
  """
98
102
  if len(qubits) != 1:
99
103
  raise ValueError(f'Expected a single qubit, got <{qubits!r}>.')
100
- from cirq.ops.pauli_string import SingleQubitPauliStringGateOperation
101
104
 
102
- return SingleQubitPauliStringGateOperation(self, qubits[0])
105
+ return pauli_string.SingleQubitPauliStringGateOperation(self, qubits[0])
103
106
 
104
107
  @property
105
108
  def _canonical_exponent(self):
cirq/ops/pauli_string.py CHANGED
@@ -42,7 +42,7 @@ import numpy as np
42
42
  import sympy
43
43
 
44
44
  import cirq
45
- from cirq import value, protocols, linalg, qis
45
+ from cirq import value, protocols, linalg, qis, _compat
46
46
  from cirq._doc import document
47
47
  from cirq._import import LazyLoader
48
48
  from cirq.ops import (
@@ -184,7 +184,7 @@ class PauliString(raw_types.Operation, Generic[TKey]):
184
184
  Raises:
185
185
  TypeError: If the `qubit_pauli_map` has values that are not Paulis.
186
186
  """
187
- if qubit_pauli_map is not None:
187
+ if _compat.__cirq_debug__.get() and qubit_pauli_map is not None:
188
188
  for v in qubit_pauli_map.values():
189
189
  if not isinstance(v, pauli_gates.Pauli):
190
190
  raise TypeError(f'{v} is not a Pauli')
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import Any, Dict, Iterable, Sequence, Tuple, TYPE_CHECKING
15
+ from typing import Any, Dict, Sequence, Tuple, TYPE_CHECKING
16
16
 
17
17
  from cirq import protocols, value
18
18
  from cirq.ops import raw_types, swap_gates
@@ -74,23 +74,21 @@ class QubitPermutationGate(raw_types.Gate):
74
74
  return True
75
75
 
76
76
  def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE':
77
- n = len(qubits)
78
- qubit_ids = [*range(n)]
79
- is_sorted = False
80
-
81
- def _swap_if_out_of_order(idx: int) -> Iterable['cirq.Operation']:
82
- nonlocal is_sorted
83
- if self._permutation[qubit_ids[idx]] > self._permutation[qubit_ids[idx + 1]]:
84
- yield swap_gates.SWAP(qubits[idx], qubits[idx + 1])
85
- qubit_ids[idx + 1], qubit_ids[idx] = qubit_ids[idx], qubit_ids[idx + 1]
86
- is_sorted = False
87
-
88
- while not is_sorted:
89
- is_sorted = True
90
- for i in range(0, n - 1, 2):
91
- yield from _swap_if_out_of_order(i)
92
- for i in range(1, n - 1, 2):
93
- yield from _swap_if_out_of_order(i)
77
+ permutation = [p for p in self.permutation]
78
+
79
+ for i in range(len(permutation)):
80
+
81
+ if permutation[i] == -1:
82
+ continue
83
+ cycle = [i]
84
+ while permutation[cycle[-1]] != i:
85
+ cycle.append(permutation[cycle[-1]])
86
+
87
+ for j in cycle:
88
+ permutation[j] = -1
89
+
90
+ for idx in cycle[1:]:
91
+ yield swap_gates.SWAP(qubits[cycle[0]], qubits[idx])
94
92
 
95
93
  def _apply_unitary_(self, args: 'cirq.ApplyUnitaryArgs'):
96
94
  # Compute the permutation index list.
@@ -34,9 +34,7 @@ def test_phased_iswap_init():
34
34
 
35
35
  def test_phased_iswap_equality():
36
36
  eq = cirq.testing.EqualsTester()
37
- eq.add_equality_group(
38
- cirq.PhasedISwapPowGate(phase_exponent=0, exponent=0.4), cirq.ISWAP**0.4
39
- )
37
+ eq.add_equality_group(cirq.PhasedISwapPowGate(phase_exponent=0, exponent=0.4), cirq.ISWAP**0.4)
40
38
  eq.add_equality_group(
41
39
  cirq.PhasedISwapPowGate(phase_exponent=0, exponent=0.4, global_shift=0.3),
42
40
  cirq.ISwapPowGate(global_shift=0.3) ** 0.4,
cirq/ops/phased_x_gate.py CHANGED
@@ -28,7 +28,7 @@ from cirq.type_workarounds import NotImplementedType
28
28
 
29
29
  @value.value_equality(manual_cls=True, approximate=True)
30
30
  class PhasedXPowGate(raw_types.Gate):
31
- r"""A gate equivalent to $Z^{p} X^t Z^{-p}$.
31
+ r"""A gate equivalent to $Z^{-p} X^t Z^{p}$ (in time order).
32
32
 
33
33
  The unitary matrix of `cirq.PhasedXPowGate(exponent=t, phase_exponent=p)` is:
34
34
  $$
@@ -27,7 +27,7 @@ if TYPE_CHECKING:
27
27
 
28
28
  @value.value_equality(approximate=True)
29
29
  class PhasedXZGate(raw_types.Gate):
30
- r"""A single qubit gate equivalent to the circuit $Z^z Z^{a} X^x Z^{-a}$.
30
+ r"""A single qubit gate equivalent to the circuit $Z^{-a} X^x Z^{a} Z^z$ (in time order).
31
31
 
32
32
  The unitary matrix of `cirq.PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a)` is:
33
33
  $$
@@ -67,6 +67,22 @@ class PhasedXZGate(raw_types.Gate):
67
67
  self._z_exponent = z_exponent
68
68
  self._axis_phase_exponent = axis_phase_exponent
69
69
 
70
+ @classmethod
71
+ def from_zyz_angles(cls, z0_rad: float, y_rad: float, z1_rad: float) -> 'cirq.PhasedXZGate':
72
+ """Create a PhasedXZGate from ZYZ angles.
73
+
74
+ The returned gate is equivalent to $Rz(z0_rad) Ry(y_rad) Rz(z1_rad)$ (in time order).
75
+ """
76
+ return cls.from_zyz_exponents(z0=z0_rad / np.pi, y=y_rad / np.pi, z1=z1_rad / np.pi)
77
+
78
+ @classmethod
79
+ def from_zyz_exponents(cls, z0: float, y: float, z1: float) -> 'cirq.PhasedXZGate':
80
+ """Create a PhasedXZGate from ZYZ exponents.
81
+
82
+ The returned gate is equivalent to $Z^z0 Y^y Z^z1$ (in time order).
83
+ """
84
+ return PhasedXZGate(axis_phase_exponent=-z0 + 0.5, x_exponent=y, z_exponent=z0 + z1)
85
+
70
86
  def _canonical(self) -> 'cirq.PhasedXZGate':
71
87
  x = self.x_exponent
72
88
  z = self.z_exponent
@@ -34,6 +34,30 @@ def test_eq():
34
34
  eq.add_equality_group(cirq.PhasedXZGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0))
35
35
 
36
36
 
37
+ @pytest.mark.parametrize('z0_rad', [-np.pi / 5, 0, np.pi / 5, np.pi / 4, np.pi / 2, np.pi])
38
+ @pytest.mark.parametrize('y_rad', [0, np.pi / 5, np.pi / 4, np.pi / 2, np.pi])
39
+ @pytest.mark.parametrize('z1_rad', [-np.pi / 5, 0, np.pi / 5, np.pi / 4, np.pi / 2, np.pi])
40
+ def test_from_zyz_angles(z0_rad: float, y_rad: float, z1_rad: float) -> None:
41
+ q = cirq.q(0)
42
+ phxz = cirq.PhasedXZGate.from_zyz_angles(z0_rad, y_rad, z1_rad)
43
+ zyz = cirq.Circuit(cirq.rz(z0_rad).on(q), cirq.ry(y_rad).on(q), cirq.rz(z1_rad).on(q))
44
+ cirq.testing.assert_allclose_up_to_global_phase(
45
+ cirq.unitary(phxz), cirq.unitary(zyz), atol=1e-8
46
+ )
47
+
48
+
49
+ @pytest.mark.parametrize('z0', [-0.2, 0, 0.2, 0.25, 0.5, 1])
50
+ @pytest.mark.parametrize('y', [0, 0.2, 0.25, 0.5, 1])
51
+ @pytest.mark.parametrize('z1', [-0.2, 0, 0.2, 0.25, 0.5, 1])
52
+ def test_from_zyz_exponents(z0: float, y: float, z1: float) -> None:
53
+ q = cirq.q(0)
54
+ phxz = cirq.PhasedXZGate.from_zyz_exponents(z0, y, z1)
55
+ zyz = cirq.Circuit(cirq.Z(q) ** z0, cirq.Y(q) ** y, cirq.Z(q) ** z1)
56
+ cirq.testing.assert_allclose_up_to_global_phase(
57
+ cirq.unitary(phxz), cirq.unitary(zyz), atol=1e-8
58
+ )
59
+
60
+
37
61
  def test_canonicalization():
38
62
  def f(x, z, a):
39
63
  return cirq.PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a)
cirq/ops/qid_util.py CHANGED
@@ -19,18 +19,15 @@ if TYPE_CHECKING:
19
19
 
20
20
 
21
21
  @overload
22
- def q(__x: int) -> 'cirq.LineQubit':
23
- ...
22
+ def q(__x: int) -> 'cirq.LineQubit': ...
24
23
 
25
24
 
26
25
  @overload
27
- def q(__row: int, __col: int) -> 'cirq.GridQubit':
28
- ...
26
+ def q(__row: int, __col: int) -> 'cirq.GridQubit': ...
29
27
 
30
28
 
31
29
  @overload
32
- def q(__name: str) -> 'cirq.NamedQubit':
33
- ...
30
+ def q(__name: str) -> 'cirq.NamedQubit': ...
34
31
 
35
32
 
36
33
  def q(*args: Union[int, str]) -> Union['cirq.LineQubit', 'cirq.GridQubit', 'cirq.NamedQubit']:
@@ -44,8 +41,7 @@ def q(*args: Union[int, str]) -> Union['cirq.LineQubit', 'cirq.GridQubit', 'cirq
44
41
  >>> cirq.q("foo") == cirq.NamedQubit("foo")
45
42
  True
46
43
 
47
- Note that arguments should be treated as positional only, even
48
- though this is only enforceable in python 3.8 or later.
44
+ Note that arguments should be treated as positional only.
49
45
 
50
46
  Args:
51
47
  *args: One or two ints, or a single str, as described above.
cirq/ops/qubit_manager.py CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  import abc
16
16
  import dataclasses
17
- from typing import Iterable, List, TYPE_CHECKING
17
+ from typing import Iterable, List, TYPE_CHECKING, Tuple
18
18
  from cirq.ops import raw_types
19
19
 
20
20
  if TYPE_CHECKING:
@@ -41,16 +41,19 @@ class _BaseAncillaQid(raw_types.Qid):
41
41
  dim: int = 2
42
42
  prefix: str = ''
43
43
 
44
- def _comparison_key(self) -> int:
45
- return self.id
44
+ def _comparison_key(self) -> Tuple[str, int]:
45
+ return self.prefix, self.id
46
46
 
47
47
  @property
48
48
  def dimension(self) -> int:
49
49
  return self.dim
50
50
 
51
+ def with_dimension(self, dimension: int) -> '_BaseAncillaQid':
52
+ return dataclasses.replace(self, dim=dimension)
53
+
51
54
  def __repr__(self) -> str:
52
55
  dim_str = f', dim={self.dim}' if self.dim != 2 else ''
53
- prefix_str = f', prefix={self.prefix}' if self.prefix != '' else ''
56
+ prefix_str = f', prefix={self.prefix!r}' if self.prefix != '' else ''
54
57
  return f"cirq.ops.{type(self).__name__}({self.id}{dim_str}{prefix_str})"
55
58
 
56
59
 
@@ -27,12 +27,27 @@ def test_clean_qubits():
27
27
  q = cqi.CleanQubit(2, dim=3)
28
28
  assert q.id == 2
29
29
  assert q.dimension == 3
30
+ assert q.with_dimension(4) == cqi.CleanQubit(2, dim=4)
30
31
  assert str(q) == '_c(2) (d=3)'
31
32
  assert repr(q) == 'cirq.ops.CleanQubit(2, dim=3)'
32
33
 
34
+ q = cqi.CleanQubit(3, dim=4, prefix="a")
35
+ assert str(q) == 'a_c(3) (d=4)'
36
+ assert repr(q) == "cirq.ops.CleanQubit(3, dim=4, prefix='a')"
37
+
33
38
  assert cqi.CleanQubit(1) < cqi.CleanQubit(2)
34
39
 
35
40
 
41
+ def test_ancilla_qubits_prefix():
42
+ assert cqi.CleanQubit(1, prefix="1") != cqi.CleanQubit(1, prefix="2")
43
+ assert cqi.CleanQubit(1, prefix="1") < cqi.CleanQubit(1, prefix="2")
44
+ assert cqi.CleanQubit(1, prefix="1") < cqi.CleanQubit(2, prefix="1")
45
+ assert cqi.BorrowableQubit(1, prefix="1") != cqi.BorrowableQubit(1, prefix="2")
46
+ assert cqi.BorrowableQubit(1, prefix="1") < cqi.BorrowableQubit(1, prefix="2")
47
+ assert cqi.BorrowableQubit(1, prefix="1") < cqi.BorrowableQubit(2, prefix="1")
48
+ assert cqi.CleanQubit(1, prefix="1") != cqi.BorrowableQubit(1, prefix="1")
49
+
50
+
36
51
  def test_borrow_qubits():
37
52
  q = cqi.BorrowableQubit(10)
38
53
  assert q.id == 10
@@ -43,9 +58,14 @@ def test_borrow_qubits():
43
58
  q = cqi.BorrowableQubit(20, dim=4)
44
59
  assert q.id == 20
45
60
  assert q.dimension == 4
61
+ assert q.with_dimension(10) == cqi.BorrowableQubit(20, dim=10)
46
62
  assert str(q) == '_b(20) (d=4)'
47
63
  assert repr(q) == 'cirq.ops.BorrowableQubit(20, dim=4)'
48
64
 
65
+ q = cqi.BorrowableQubit(30, dim=4, prefix="a")
66
+ assert str(q) == 'a_b(30) (d=4)'
67
+ assert repr(q) == "cirq.ops.BorrowableQubit(30, dim=4, prefix='a')"
68
+
49
69
  assert cqi.BorrowableQubit(1) < cqi.BorrowableQubit(2)
50
70
 
51
71
 
cirq/ops/raw_types.py CHANGED
@@ -230,7 +230,7 @@ class Gate(metaclass=value.ABCMetaImplementAnyOneOf):
230
230
  Returns: a `cirq.Operation` which is this gate applied to the given
231
231
  qubits.
232
232
  """
233
- return ops.gate_operation.GateOperation(self, list(qubits))
233
+ return ops.gate_operation.GateOperation(self, qubits)
234
234
 
235
235
  def on_each(self, *targets: Union[Qid, Iterable[Any]]) -> List['cirq.Operation']:
236
236
  """Returns a list of operations applying the gate to all targets.
@@ -921,7 +921,7 @@ class TaggedOperation(Operation):
921
921
  # Add tag to wire symbol if it exists.
922
922
  if sub_op_info is not NotImplemented and args.include_tags and sub_op_info.wire_symbols:
923
923
  sub_op_info.wire_symbols = (
924
- sub_op_info.wire_symbols[0] + str(list(self._tags)),
924
+ sub_op_info.wire_symbols[0] + f"[{', '.join(map(str, self._tags))}]",
925
925
  ) + sub_op_info.wire_symbols[1:]
926
926
  return sub_op_info
927
927
 
@@ -1032,6 +1032,9 @@ class _InverseCompositeGate(Gate):
1032
1032
  def __repr__(self) -> str:
1033
1033
  return f'({self._original!r}**-1)'
1034
1034
 
1035
+ def __str__(self) -> str:
1036
+ return f'{self._original!s}†'
1037
+
1035
1038
 
1036
1039
  def _validate_qid_shape(val: Any, qubits: Sequence['cirq.Qid']) -> None:
1037
1040
  """Helper function to validate qubits for gates and operations.