cirq-core 1.2.0.dev20230717232332__py3-none-any.whl → 1.3.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.
Files changed (158) hide show
  1. cirq/__init__.py +5 -0
  2. cirq/_compat.py +26 -11
  3. cirq/_compat_test.py +37 -3
  4. cirq/_version.py +31 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/circuit.py +106 -32
  7. cirq/circuits/circuit_operation.py +2 -2
  8. cirq/circuits/circuit_operation_test.py +1 -1
  9. cirq/circuits/circuit_test.py +109 -3
  10. cirq/circuits/frozen_circuit.py +80 -5
  11. cirq/circuits/frozen_circuit_test.py +47 -2
  12. cirq/circuits/qasm_output_test.py +9 -9
  13. cirq/conftest.py +1 -2
  14. cirq/contrib/acquaintance/devices.py +1 -1
  15. cirq/contrib/hacks/disable_validation_test.py +1 -1
  16. cirq/contrib/noise_models/noise_models.py +1 -2
  17. cirq/contrib/paulistring/clifford_optimize.py +1 -1
  18. cirq/contrib/paulistring/clifford_target_gateset_test.py +4 -4
  19. cirq/contrib/qcircuit/qcircuit_pdf.py +1 -1
  20. cirq/contrib/quimb/density_matrix.py +2 -3
  21. cirq/contrib/quimb/grid_circuits.py +3 -3
  22. cirq/contrib/quimb/state_vector.py +3 -5
  23. cirq/contrib/routing/utils.py +1 -2
  24. cirq/contrib/svg/svg.py +4 -6
  25. cirq/devices/grid_qubit.py +49 -38
  26. cirq/devices/grid_qubit_test.py +1 -3
  27. cirq/devices/insertion_noise_model.py +21 -1
  28. cirq/devices/insertion_noise_model_test.py +6 -0
  29. cirq/devices/line_qubit.py +67 -40
  30. cirq/devices/named_topologies.py +8 -14
  31. cirq/devices/noise_properties.py +1 -1
  32. cirq/devices/noise_utils.py +7 -5
  33. cirq/devices/noise_utils_test.py +7 -0
  34. cirq/experiments/fidelity_estimation_test.py +1 -1
  35. cirq/experiments/qubit_characterizations.py +6 -5
  36. cirq/experiments/random_quantum_circuit_generation.py +1 -1
  37. cirq/experiments/random_quantum_circuit_generation_test.py +28 -1
  38. cirq/experiments/readout_confusion_matrix.py +6 -6
  39. cirq/experiments/xeb_fitting.py +3 -5
  40. cirq/experiments/xeb_fitting_test.py +2 -2
  41. cirq/experiments/xeb_sampling.py +1 -1
  42. cirq/interop/quirk/url_to_circuit.py +40 -38
  43. cirq/json_resolver_cache.py +2 -0
  44. cirq/linalg/decompositions.py +6 -5
  45. cirq/ops/__init__.py +2 -0
  46. cirq/ops/classically_controlled_operation.py +1 -1
  47. cirq/ops/clifford_gate.py +9 -9
  48. cirq/ops/clifford_gate_test.py +3 -4
  49. cirq/ops/common_channels.py +2 -5
  50. cirq/ops/common_channels_test.py +3 -5
  51. cirq/ops/common_gates_test.py +7 -7
  52. cirq/ops/controlled_operation_test.py +2 -2
  53. cirq/ops/dense_pauli_string.py +3 -0
  54. cirq/ops/eigen_gate_test.py +1 -3
  55. cirq/ops/fourier_transform.py +1 -2
  56. cirq/ops/fsim_gate.py +1 -1
  57. cirq/ops/gate_features_test.py +2 -2
  58. cirq/ops/gate_operation_test.py +1 -2
  59. cirq/ops/greedy_qubit_manager.py +86 -0
  60. cirq/ops/greedy_qubit_manager_test.py +98 -0
  61. cirq/ops/linear_combinations.py +1 -1
  62. cirq/ops/named_qubit.py +55 -18
  63. cirq/ops/parity_gates.py +65 -18
  64. cirq/ops/parity_gates_test.py +41 -2
  65. cirq/ops/pauli_gates.py +2 -2
  66. cirq/ops/pauli_string.py +3 -4
  67. cirq/ops/pauli_string_raw_types_test.py +3 -3
  68. cirq/ops/pauli_string_test.py +3 -4
  69. cirq/ops/random_gate_channel_test.py +3 -3
  70. cirq/ops/raw_types.py +1 -1
  71. cirq/ops/raw_types_test.py +5 -5
  72. cirq/ops/three_qubit_gates.py +12 -8
  73. cirq/protocols/act_on_protocol_test.py +9 -9
  74. cirq/protocols/apply_channel_protocol.py +9 -6
  75. cirq/protocols/apply_unitary_protocol_test.py +1 -1
  76. cirq/protocols/equal_up_to_global_phase_protocol_test.py +2 -2
  77. cirq/protocols/has_stabilizer_effect_protocol.py +52 -6
  78. cirq/protocols/has_stabilizer_effect_protocol_test.py +21 -8
  79. cirq/protocols/has_unitary_protocol_test.py +1 -3
  80. cirq/protocols/json_serialization.py +6 -6
  81. cirq/protocols/json_serialization_test.py +7 -14
  82. cirq/protocols/json_test_data/InsertionNoiseModel.json +91 -0
  83. cirq/protocols/json_test_data/InsertionNoiseModel.repr +4 -0
  84. cirq/protocols/json_test_data/OpIdentifier.json +45 -10
  85. cirq/protocols/json_test_data/OpIdentifier.repr +7 -1
  86. cirq/protocols/json_test_data/spec.py +4 -0
  87. cirq/protocols/measurement_key_protocol_test.py +1 -1
  88. cirq/protocols/unitary_protocol_test.py +13 -16
  89. cirq/qis/clifford_tableau.py +7 -8
  90. cirq/qis/measures.py +1 -1
  91. cirq/qis/states.py +2 -3
  92. cirq/sim/__init__.py +2 -0
  93. cirq/sim/classical_simulator.py +107 -0
  94. cirq/sim/classical_simulator_test.py +207 -0
  95. cirq/sim/clifford/clifford_simulator_test.py +7 -7
  96. cirq/sim/clifford/stabilizer_simulation_state.py +2 -2
  97. cirq/sim/clifford/stabilizer_state_ch_form.py +7 -7
  98. cirq/sim/density_matrix_simulation_state.py +19 -4
  99. cirq/sim/density_matrix_simulator_test.py +5 -13
  100. cirq/sim/simulation_state_test.py +13 -14
  101. cirq/sim/simulator_test.py +6 -9
  102. cirq/sim/state_vector_simulation_state.py +1 -1
  103. cirq/study/resolver.py +41 -41
  104. cirq/study/resolver_test.py +13 -12
  105. cirq/testing/__init__.py +4 -1
  106. cirq/testing/circuit_compare.py +1 -1
  107. cirq/testing/circuit_compare_test.py +11 -11
  108. cirq/testing/consistent_controlled_gate_op.py +15 -1
  109. cirq/testing/consistent_controlled_gate_op_test.py +12 -3
  110. cirq/testing/consistent_decomposition.py +0 -1
  111. cirq/testing/consistent_protocols.py +6 -1
  112. cirq/testing/consistent_protocols_test.py +5 -10
  113. cirq/testing/consistent_qasm.py +2 -4
  114. cirq/testing/consistent_qasm_test.py +2 -3
  115. cirq/testing/consistent_specified_has_unitary_test.py +1 -3
  116. cirq/testing/equals_tester.py +1 -1
  117. cirq/testing/equals_tester_test.py +5 -5
  118. cirq/testing/equivalent_repr_eval_test.py +1 -3
  119. cirq/testing/gate_features_test.py +6 -6
  120. cirq/testing/order_tester_test.py +1 -3
  121. cirq/testing/random_circuit_test.py +1 -3
  122. cirq/transformers/__init__.py +3 -0
  123. cirq/transformers/analytical_decompositions/__init__.py +1 -0
  124. cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +1 -2
  125. cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +2 -5
  126. cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +38 -0
  127. cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +18 -0
  128. cirq/transformers/expand_composite_test.py +4 -4
  129. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +1 -1
  130. cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -2
  131. cirq/transformers/merge_k_qubit_gates_test.py +2 -2
  132. cirq/transformers/qubit_management_transformers.py +177 -0
  133. cirq/transformers/qubit_management_transformers_test.py +250 -0
  134. cirq/transformers/routing/route_circuit_cqc.py +23 -4
  135. cirq/transformers/routing/route_circuit_cqc_test.py +42 -0
  136. cirq/transformers/stratify.py +10 -11
  137. cirq/transformers/target_gatesets/compilation_target_gateset_test.py +10 -10
  138. cirq/transformers/target_gatesets/cz_gateset_test.py +8 -10
  139. cirq/transformers/transformer_primitives.py +138 -28
  140. cirq/value/abc_alt_test.py +4 -4
  141. cirq/value/duration.py +68 -37
  142. cirq/value/duration_test.py +2 -0
  143. cirq/value/measurement_key_test.py +1 -1
  144. cirq/value/product_state.py +4 -8
  145. cirq/value/value_equality_attr.py +12 -5
  146. cirq/vis/heatmap.py +7 -4
  147. cirq/vis/heatmap_test.py +14 -4
  148. cirq/vis/histogram.py +4 -4
  149. cirq/vis/state_histogram.py +10 -6
  150. cirq/vis/state_histogram_test.py +2 -0
  151. cirq/work/observable_measurement_data_test.py +1 -1
  152. cirq/work/observable_measurement_test.py +2 -2
  153. cirq/work/zeros_sampler.py +1 -1
  154. {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/METADATA +11 -19
  155. {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/RECORD +158 -150
  156. {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/WHEEL +1 -1
  157. {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/LICENSE +0 -0
  158. {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,98 @@
1
+ # Copyright 2023 The Cirq Developers
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import cirq
16
+
17
+
18
+ class GateAllocInDecompose(cirq.Gate):
19
+ def __init__(self, num_alloc: int = 1):
20
+ self.num_alloc = num_alloc
21
+
22
+ def _num_qubits_(self) -> int:
23
+ return 1
24
+
25
+ def _decompose_with_context_(self, qubits, context):
26
+ assert context is not None
27
+ qm = context.qubit_manager
28
+ for q in qm.qalloc(self.num_alloc):
29
+ yield cirq.CNOT(qubits[0], q)
30
+ qm.qfree([q])
31
+
32
+
33
+ def test_greedy_qubit_manager():
34
+ def make_circuit(qm: cirq.QubitManager):
35
+ q = cirq.LineQubit.range(2)
36
+ g = GateAllocInDecompose(1)
37
+ context = cirq.DecompositionContext(qubit_manager=qm)
38
+ circuit = cirq.Circuit(
39
+ cirq.decompose_once(g.on(q[0]), context=context),
40
+ cirq.decompose_once(g.on(q[1]), context=context),
41
+ )
42
+ return circuit
43
+
44
+ qm = cirq.GreedyQubitManager(prefix="ancilla", size=1)
45
+ # Qubit manager with only 1 managed qubit. Will always repeat the same qubit.
46
+ circuit = make_circuit(qm)
47
+ cirq.testing.assert_has_diagram(
48
+ circuit,
49
+ """
50
+ 0: ───────────@───────
51
+
52
+ 1: ───────────┼───@───
53
+ │ │
54
+ ancilla_0: ───X───X───
55
+ """,
56
+ )
57
+
58
+ qm = cirq.GreedyQubitManager(prefix="ancilla", size=2)
59
+ # Qubit manager with 2 managed qubits and maximize_reuse=False, tries to minimize adding
60
+ # additional data dependencies by minimizing qubit reuse.
61
+ circuit = make_circuit(qm)
62
+ cirq.testing.assert_has_diagram(
63
+ circuit,
64
+ """
65
+ ┌──┐
66
+ 0: ────────────@─────
67
+
68
+ 1: ────────────┼@────
69
+ ││
70
+ ancilla_0: ────X┼────
71
+
72
+ ancilla_1: ─────X────
73
+ └──┘
74
+ """,
75
+ )
76
+
77
+ qm = cirq.GreedyQubitManager(prefix="ancilla", size=2, maximize_reuse=True)
78
+ # Qubit manager with 2 managed qubits and maximize_reuse=True, tries to maximize reuse by
79
+ # potentially adding new data dependencies.
80
+ circuit = make_circuit(qm)
81
+ cirq.testing.assert_has_diagram(
82
+ circuit,
83
+ """
84
+ 0: ───────────@───────
85
+
86
+ 1: ───────────┼───@───
87
+ │ │
88
+ ancilla_1: ───X───X───
89
+ """,
90
+ )
91
+
92
+
93
+ def test_greedy_qubit_manager_preserves_order():
94
+ qm = cirq.GreedyQubitManager(prefix="anc")
95
+ ancillae = [cirq.q(f"anc_{i}") for i in range(100)]
96
+ assert qm.qalloc(100) == ancillae
97
+ qm.qfree(ancillae)
98
+ assert qm.qalloc(100) == ancillae
@@ -357,7 +357,7 @@ def _pauli_string_from_unit(unit: UnitPauliStringT, coefficient: Union[int, floa
357
357
  return PauliString(qubit_pauli_map=dict(unit), coefficient=coefficient)
358
358
 
359
359
 
360
- @value.value_equality(approximate=True)
360
+ @value.value_equality(approximate=True, unhashable=True)
361
361
  class PauliSum:
362
362
  """Represents operator defined by linear combination of PauliStrings.
363
363
 
cirq/ops/named_qubit.py CHANGED
@@ -12,7 +12,7 @@
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, TYPE_CHECKING
15
+ from typing import Any, Dict, List, Optional, TYPE_CHECKING
16
16
 
17
17
  from cirq import protocols
18
18
  from cirq.ops import raw_types
@@ -26,17 +26,52 @@ if TYPE_CHECKING:
26
26
  class _BaseNamedQid(raw_types.Qid):
27
27
  """The base class for `NamedQid` and `NamedQubit`."""
28
28
 
29
- def __init__(self, name: str) -> None:
30
- self._name = name
31
- self._comp_key = _pad_digits(name)
29
+ _name: str
30
+ _dimension: int
31
+ _comp_key: Optional[str] = None
32
+ _hash: Optional[int] = None
33
+
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
+ def __hash__(self) -> int:
46
+ if self._hash is None:
47
+ self._hash = hash((self._name, self._dimension))
48
+ return self._hash
49
+
50
+ def __eq__(self, other):
51
+ # Explicitly implemented for performance (vs delegating to Qid).
52
+ if isinstance(other, _BaseNamedQid):
53
+ return self._name == other._name and self._dimension == other._dimension
54
+ return NotImplemented
55
+
56
+ def __ne__(self, other):
57
+ # Explicitly implemented for performance (vs delegating to Qid).
58
+ if isinstance(other, _BaseNamedQid):
59
+ return self._name != other._name or self._dimension != other._dimension
60
+ return NotImplemented
32
61
 
33
62
  def _comparison_key(self):
63
+ if self._comp_key is None:
64
+ self._comp_key = _pad_digits(self._name)
34
65
  return self._comp_key
35
66
 
36
67
  @property
37
68
  def name(self) -> str:
38
69
  return self._name
39
70
 
71
+ @property
72
+ def dimension(self) -> int:
73
+ return self._dimension
74
+
40
75
  def with_dimension(self, dimension: int) -> 'NamedQid':
41
76
  return NamedQid(self._name, dimension=dimension)
42
77
 
@@ -59,19 +94,15 @@ class NamedQid(_BaseNamedQid):
59
94
  dimension: The dimension of the qid's Hilbert space, i.e.
60
95
  the number of quantum levels.
61
96
  """
62
- super().__init__(name)
63
- self._dimension = dimension
64
97
  self.validate_dimension(dimension)
65
-
66
- @property
67
- def dimension(self) -> int:
68
- return self._dimension
98
+ self._name = name
99
+ self._dimension = dimension
69
100
 
70
101
  def __repr__(self) -> str:
71
- return f'cirq.NamedQid({self.name!r}, dimension={self.dimension})'
102
+ return f'cirq.NamedQid({self._name!r}, dimension={self._dimension})'
72
103
 
73
104
  def __str__(self) -> str:
74
- return f'{self.name} (d={self.dimension})'
105
+ return f'{self._name} (d={self._dimension})'
75
106
 
76
107
  @staticmethod
77
108
  def range(*args, prefix: str, dimension: int) -> List['NamedQid']:
@@ -95,7 +126,7 @@ class NamedQid(_BaseNamedQid):
95
126
  Returns:
96
127
  A list of ``NamedQid``\\s.
97
128
  """
98
- return [NamedQid(prefix + str(i), dimension=dimension) for i in range(*args)]
129
+ return [NamedQid(f"{prefix}{i}", dimension=dimension) for i in range(*args)]
99
130
 
100
131
  def _json_dict_(self) -> Dict[str, Any]:
101
132
  return protocols.obj_to_dict_helper(self, ['name', 'dimension'])
@@ -110,14 +141,20 @@ class NamedQubit(_BaseNamedQid):
110
141
  wire for 'qubit3' will correctly come before 'qubit22'.
111
142
  """
112
143
 
113
- @property
114
- def dimension(self) -> int:
115
- return 2
144
+ _dimension = 2
145
+
146
+ def __init__(self, name: str) -> None:
147
+ """Initializes a `NamedQubit` with a given name.
148
+
149
+ Args:
150
+ name: The name.
151
+ """
152
+ self._name = name
116
153
 
117
154
  def _cmp_tuple(self):
118
155
  cls = NamedQid if type(self) is NamedQubit else type(self)
119
156
  # Must be same as Qid._cmp_tuple but with cls in place of type(self).
120
- return (cls.__name__, repr(cls), self._comparison_key(), self.dimension)
157
+ return (cls.__name__, repr(cls), self._comparison_key(), self._dimension)
121
158
 
122
159
  def __str__(self) -> str:
123
160
  return self._name
@@ -146,7 +183,7 @@ class NamedQubit(_BaseNamedQid):
146
183
  Returns:
147
184
  A list of ``NamedQubit``\\s.
148
185
  """
149
- return [NamedQubit(prefix + str(i)) for i in range(*args)]
186
+ return [NamedQubit(f"{prefix}{i}") for i in range(*args)]
150
187
 
151
188
  def _json_dict_(self) -> Dict[str, Any]:
152
189
  return protocols.obj_to_dict_helper(self, ['name'])
cirq/ops/parity_gates.py CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  """Quantum gates that phase with respect to product-of-pauli observables."""
16
16
 
17
- from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING
17
+ from typing import Any, Dict, List, Optional, Tuple, Union, TYPE_CHECKING, Sequence
18
18
  from typing_extensions import Self
19
19
 
20
20
  import numpy as np
@@ -22,12 +22,21 @@ import numpy as np
22
22
  from cirq import protocols, value
23
23
  from cirq._compat import proper_repr
24
24
  from cirq._doc import document
25
- from cirq.ops import gate_features, eigen_gate, common_gates, pauli_gates
25
+ from cirq.ops import (
26
+ gate_features,
27
+ eigen_gate,
28
+ common_gates,
29
+ pauli_gates,
30
+ clifford_gate,
31
+ pauli_interaction_gate,
32
+ )
33
+
26
34
 
27
35
  if TYPE_CHECKING:
28
36
  import cirq
29
37
 
30
38
 
39
+ @value.value_equality
31
40
  class XXPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
32
41
  r"""The X-parity gate, possibly raised to a power.
33
42
 
@@ -86,25 +95,29 @@ class XXPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
86
95
  return abs(np.sin(self._exponent * 0.5 * np.pi))
87
96
 
88
97
  def _decompose_into_clifford_with_qubits_(self, qubits):
89
- from cirq.ops.clifford_gate import SingleQubitCliffordGate
90
- from cirq.ops.pauli_interaction_gate import PauliInteractionGate
91
-
92
98
  if self.exponent % 2 == 0:
93
99
  return []
94
100
  if self.exponent % 2 == 0.5:
95
101
  return [
96
- PauliInteractionGate(pauli_gates.X, False, pauli_gates.X, False).on(*qubits),
97
- SingleQubitCliffordGate.X_sqrt.on_each(*qubits),
102
+ pauli_interaction_gate.PauliInteractionGate(
103
+ pauli_gates.X, False, pauli_gates.X, False
104
+ ).on(*qubits),
105
+ clifford_gate.SingleQubitCliffordGate.X_sqrt.on_each(*qubits),
98
106
  ]
99
107
  if self.exponent % 2 == 1:
100
- return [SingleQubitCliffordGate.X.on_each(*qubits)]
108
+ return [clifford_gate.SingleQubitCliffordGate.X.on_each(*qubits)]
101
109
  if self.exponent % 2 == 1.5:
102
110
  return [
103
- PauliInteractionGate(pauli_gates.X, False, pauli_gates.X, False).on(*qubits),
104
- SingleQubitCliffordGate.X_nsqrt.on_each(*qubits),
111
+ pauli_interaction_gate.PauliInteractionGate(
112
+ pauli_gates.X, False, pauli_gates.X, False
113
+ ).on(*qubits),
114
+ clifford_gate.SingleQubitCliffordGate.X_nsqrt.on_each(*qubits),
105
115
  ]
106
116
  return NotImplemented
107
117
 
118
+ def _has_stabilizer_effect_(self) -> bool:
119
+ return self.exponent % 2 in (0, 0.5, 1, 1.5)
120
+
108
121
  def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
109
122
  yield common_gates.YPowGate(exponent=-0.5).on_each(*qubits)
110
123
  yield ZZPowGate(exponent=self.exponent, global_shift=self.global_shift)(*qubits)
@@ -133,6 +146,7 @@ class XXPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
133
146
  )
134
147
 
135
148
 
149
+ @value.value_equality
136
150
  class YYPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
137
151
  r"""The Y-parity gate, possibly raised to a power.
138
152
 
@@ -190,25 +204,29 @@ class YYPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
190
204
  return abs(np.sin(self._exponent * 0.5 * np.pi))
191
205
 
192
206
  def _decompose_into_clifford_with_qubits_(self, qubits):
193
- from cirq.ops.clifford_gate import SingleQubitCliffordGate
194
- from cirq.ops.pauli_interaction_gate import PauliInteractionGate
195
-
196
207
  if self.exponent % 2 == 0:
197
208
  return []
198
209
  if self.exponent % 2 == 0.5:
199
210
  return [
200
- PauliInteractionGate(pauli_gates.Y, False, pauli_gates.Y, False).on(*qubits),
201
- SingleQubitCliffordGate.Y_sqrt.on_each(*qubits),
211
+ pauli_interaction_gate.PauliInteractionGate(
212
+ pauli_gates.Y, False, pauli_gates.Y, False
213
+ ).on(*qubits),
214
+ clifford_gate.SingleQubitCliffordGate.Y_sqrt.on_each(*qubits),
202
215
  ]
203
216
  if self.exponent % 2 == 1:
204
- return [SingleQubitCliffordGate.Y.on_each(*qubits)]
217
+ return [clifford_gate.SingleQubitCliffordGate.Y.on_each(*qubits)]
205
218
  if self.exponent % 2 == 1.5:
206
219
  return [
207
- PauliInteractionGate(pauli_gates.Y, False, pauli_gates.Y, False).on(*qubits),
208
- SingleQubitCliffordGate.Y_nsqrt.on_each(*qubits),
220
+ pauli_interaction_gate.PauliInteractionGate(
221
+ pauli_gates.Y, False, pauli_gates.Y, False
222
+ ).on(*qubits),
223
+ clifford_gate.SingleQubitCliffordGate.Y_nsqrt.on_each(*qubits),
209
224
  ]
210
225
  return NotImplemented
211
226
 
227
+ def _has_stabilizer_effect_(self) -> bool:
228
+ return self.exponent % 2 in (0, 0.5, 1, 1.5)
229
+
212
230
  def _decompose_(self, qubits: Tuple['cirq.Qid', ...]) -> 'cirq.OP_TREE':
213
231
  yield common_gates.XPowGate(exponent=0.5).on_each(*qubits)
214
232
  yield ZZPowGate(exponent=self.exponent, global_shift=self.global_shift)(*qubits)
@@ -237,6 +255,7 @@ class YYPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
237
255
  )
238
256
 
239
257
 
258
+ @value.value_equality
240
259
  class ZZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
241
260
  r"""The Z-parity gate, possibly raised to a power.
242
261
 
@@ -262,6 +281,34 @@ class ZZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
262
281
  exponent=-2 * self.exponent, global_shift=-self.global_shift / 2
263
282
  )(qubits[0], qubits[1])
264
283
 
284
+ def _decompose_into_clifford_with_qubits_(
285
+ self, qubits: Sequence['cirq.Qid']
286
+ ) -> Sequence[Union['cirq.Operation', Sequence['cirq.Operation']]]:
287
+ if not self._has_stabilizer_effect_():
288
+ return NotImplemented
289
+ if self.exponent % 2 == 0:
290
+ return []
291
+ if self.exponent % 2 == 1:
292
+ return clifford_gate.SingleQubitCliffordGate.Z.on_each(*qubits)
293
+
294
+ if self.exponent % 2 == 0.5:
295
+ return [
296
+ pauli_interaction_gate.PauliInteractionGate(
297
+ pauli_gates.Z, False, pauli_gates.Z, False
298
+ ).on(*qubits),
299
+ clifford_gate.SingleQubitCliffordGate.Z_sqrt.on_each(*qubits),
300
+ ]
301
+ else:
302
+ return [
303
+ pauli_interaction_gate.PauliInteractionGate(
304
+ pauli_gates.Z, False, pauli_gates.Z, False
305
+ ).on(*qubits),
306
+ clifford_gate.SingleQubitCliffordGate.Z_nsqrt.on_each(*qubits),
307
+ ]
308
+
309
+ def _has_stabilizer_effect_(self) -> bool:
310
+ return self.exponent % 2 in (0, 0.5, 1, 1.5)
311
+
265
312
  def _eigen_components(self) -> List[Tuple[float, np.ndarray]]:
266
313
  return [(0, np.diag([1, 0, 0, 1])), (1, np.diag([0, 1, 1, 0]))]
267
314
 
@@ -257,8 +257,24 @@ def test_trace_distance():
257
257
 
258
258
  def test_ms_arguments():
259
259
  eq_tester = cirq.testing.EqualsTester()
260
- eq_tester.add_equality_group(cirq.ms(np.pi / 2), cirq.ops.MSGate(rads=np.pi / 2))
261
- eq_tester.add_equality_group(cirq.XXPowGate(global_shift=-0.5))
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)
262
+ )
263
+ eq_tester.add_equality_group(
264
+ cirq.ms(np.pi / 4), cirq.XXPowGate(exponent=0.5, global_shift=-0.5)
265
+ )
266
+ eq_tester.add_equality_group(cirq.XX)
267
+ eq_tester.add_equality_group(cirq.XX**0.5)
268
+
269
+
270
+ def test_ms_equal_up_to_global_phase():
271
+ assert cirq.equal_up_to_global_phase(cirq.ms(np.pi / 2), cirq.XX)
272
+ assert cirq.equal_up_to_global_phase(cirq.ms(np.pi / 4), cirq.XX**0.5)
273
+ assert not cirq.equal_up_to_global_phase(cirq.ms(np.pi / 4), cirq.XX)
274
+
275
+ assert cirq.ms(np.pi / 2) in cirq.GateFamily(cirq.XX)
276
+ assert cirq.ms(np.pi / 4) in cirq.GateFamily(cirq.XX**0.5)
277
+ assert cirq.ms(np.pi / 4) not in cirq.GateFamily(cirq.XX)
262
278
 
263
279
 
264
280
  def test_ms_str():
@@ -316,3 +332,26 @@ def test_json_serialization():
316
332
  json_text=cirq.to_json(cirq.ms(np.pi / 2)), resolvers=[custom_resolver]
317
333
  ) == cirq.ms(np.pi / 2)
318
334
  assert custom_resolver('X') is None
335
+
336
+
337
+ @pytest.mark.parametrize('gate_cls', (cirq.XXPowGate, cirq.YYPowGate, cirq.ZZPowGate))
338
+ @pytest.mark.parametrize(
339
+ 'exponent,is_clifford',
340
+ ((0, True), (0.5, True), (0.75, False), (1, True), (1.5, True), (-1.5, True)),
341
+ )
342
+ def test_clifford_protocols(gate_cls: type[cirq.EigenGate], exponent: float, is_clifford: bool):
343
+ gate = gate_cls(exponent=exponent)
344
+ assert hasattr(gate, '_decompose_into_clifford_with_qubits_')
345
+ if is_clifford:
346
+ clifford_decomposition = cirq.Circuit(
347
+ gate._decompose_into_clifford_with_qubits_(cirq.LineQubit.range(2))
348
+ )
349
+ assert cirq.has_stabilizer_effect(gate)
350
+ assert cirq.has_stabilizer_effect(clifford_decomposition)
351
+ if exponent == 0:
352
+ assert clifford_decomposition == cirq.Circuit()
353
+ else:
354
+ np.testing.assert_allclose(cirq.unitary(gate), cirq.unitary(clifford_decomposition))
355
+ else:
356
+ assert not cirq.has_stabilizer_effect(gate)
357
+ assert gate._decompose_into_clifford_with_qubits_(cirq.LineQubit.range(2)) is NotImplemented
cirq/ops/pauli_gates.py CHANGED
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
26
26
  _XEigenState,
27
27
  _YEigenState,
28
28
  _ZEigenState,
29
- ) # coverage: ignore
29
+ ) # pragma: no cover
30
30
 
31
31
 
32
32
  class Pauli(raw_types.Gate, metaclass=abc.ABCMeta):
@@ -186,7 +186,7 @@ document(
186
186
 
187
187
  This is the `exponent=1` instance of the `cirq.XPowGate`.
188
188
 
189
- The untary matrix of `cirq.X` is:
189
+ The unitary matrix of `cirq.X` is:
190
190
  $$
191
191
  \begin{bmatrix}
192
192
  0 & 1 \\
cirq/ops/pauli_string.py CHANGED
@@ -92,7 +92,7 @@ document(
92
92
  """,
93
93
  )
94
94
 
95
- PAULI_GATE_LIKE = Union['cirq.Pauli', 'cirq.IdentityGate', str, int,]
95
+ PAULI_GATE_LIKE = Union['cirq.Pauli', 'cirq.IdentityGate', str, int]
96
96
  document(
97
97
  PAULI_GATE_LIKE,
98
98
  """An object that can be interpreted as a Pauli gate.
@@ -1387,8 +1387,7 @@ class MutablePauliString(Generic[TKey]):
1387
1387
  if gate.invert1:
1388
1388
  self.inplace_after(gate.pauli0(q0))
1389
1389
 
1390
- else:
1391
- # coverage: ignore
1390
+ else: # pragma: no cover
1392
1391
  raise NotImplementedError(f"Unrecognized decomposed Clifford: {op!r}")
1393
1392
  return self
1394
1393
 
@@ -1617,7 +1616,7 @@ def _pass_single_clifford_gate_over(
1617
1616
  after_to_before: bool = False,
1618
1617
  ) -> bool:
1619
1618
  if qubit not in pauli_map:
1620
- return False # coverage: ignore
1619
+ return False # pragma: no cover
1621
1620
  if not after_to_before:
1622
1621
  gate **= -1
1623
1622
  pauli, inv = gate.pauli_tuple(pauli_map[qubit])
@@ -48,12 +48,12 @@ def test_op_calls_validate():
48
48
  def test_on_wrong_number_qubits():
49
49
  q0, q1, q2 = _make_qubits(3)
50
50
 
51
- class DummyGate(cirq.PauliStringGateOperation):
51
+ class ExampleGate(cirq.PauliStringGateOperation):
52
52
  def map_qubits(self, qubit_map):
53
53
  ps = self.pauli_string.map_qubits(qubit_map)
54
- return DummyGate(ps)
54
+ return ExampleGate(ps)
55
55
 
56
- g = DummyGate(cirq.PauliString({q0: cirq.X, q1: cirq.Y}))
56
+ g = ExampleGate(cirq.PauliString({q0: cirq.X, q1: cirq.Y}))
57
57
 
58
58
  _ = g.with_qubits(q1, q2)
59
59
  with pytest.raises(ValueError):
@@ -732,11 +732,11 @@ def test_pass_operations_over_cz():
732
732
 
733
733
 
734
734
  def test_pass_operations_over_no_common_qubits():
735
- class DummyGate(cirq.testing.SingleQubitGate):
735
+ class ExampleGate(cirq.testing.SingleQubitGate):
736
736
  pass
737
737
 
738
738
  q0, q1 = _make_qubits(2)
739
- op0 = DummyGate()(q1)
739
+ op0 = ExampleGate()(q1)
740
740
  ps_before = cirq.PauliString({q0: cirq.Z})
741
741
  ps_after = cirq.PauliString({q0: cirq.Z})
742
742
  _assert_pass_over([op0], ps_before, ps_after)
@@ -1765,8 +1765,7 @@ def test_mutable_pauli_string_inplace_conjugate_by():
1765
1765
  self._qubits = qubits
1766
1766
 
1767
1767
  @property
1768
- def qubits(self):
1769
- # coverage: ignore
1768
+ def qubits(self): # pragma: no cover
1770
1769
  return self._qubits
1771
1770
 
1772
1771
  def with_qubits(self, *new_qubits):
@@ -219,13 +219,13 @@ def test_stabilizer_supports_probability():
219
219
 
220
220
 
221
221
  def test_unsupported_stabilizer_safety():
222
- from cirq.protocols.act_on_protocol_test import DummySimulationState
222
+ from cirq.protocols.act_on_protocol_test import ExampleSimulationState
223
223
 
224
224
  with pytest.raises(TypeError, match="act_on"):
225
225
  for _ in range(100):
226
- cirq.act_on(cirq.X.with_probability(0.5), DummySimulationState(), qubits=())
226
+ cirq.act_on(cirq.X.with_probability(0.5), ExampleSimulationState(), qubits=())
227
227
  with pytest.raises(TypeError, match="act_on"):
228
- cirq.act_on(cirq.X.with_probability(sympy.Symbol('x')), DummySimulationState(), qubits=())
228
+ cirq.act_on(cirq.X.with_probability(sympy.Symbol('x')), ExampleSimulationState(), qubits=())
229
229
 
230
230
  q = cirq.LineQubit(0)
231
231
  c = cirq.Circuit((cirq.X(q) ** 0.25).with_probability(0.5), cirq.measure(q, key='m'))
cirq/ops/raw_types.py CHANGED
@@ -671,7 +671,7 @@ class Operation(metaclass=abc.ABCMeta):
671
671
  # Don't create gigantic matrices.
672
672
  shape = protocols.qid_shape_protocol.qid_shape(circuit12)
673
673
  if np.prod(shape, dtype=np.int64) > 2**10:
674
- return NotImplemented # coverage: ignore
674
+ return NotImplemented # pragma: no cover
675
675
 
676
676
  m12 = protocols.unitary_protocol.unitary(circuit12, default=None)
677
677
  m21 = protocols.unitary_protocol.unitary(circuit21, default=None)
@@ -356,7 +356,7 @@ def test_gate_shape_protocol():
356
356
  def test_operation_shape():
357
357
  class FixedQids(cirq.Operation):
358
358
  def with_qubits(self, *new_qids):
359
- raise NotImplementedError # coverage: ignore
359
+ raise NotImplementedError # pragma: no cover
360
360
 
361
361
  class QubitOp(FixedQids):
362
362
  @property
@@ -787,9 +787,9 @@ def test_tagged_act_on():
787
787
  pass
788
788
 
789
789
  q = cirq.LineQubit(1)
790
- from cirq.protocols.act_on_protocol_test import DummySimulationState
790
+ from cirq.protocols.act_on_protocol_test import ExampleSimulationState
791
791
 
792
- args = DummySimulationState()
792
+ args = ExampleSimulationState()
793
793
  cirq.act_on(YesActOn()(q).with_tags("test"), args)
794
794
  with pytest.raises(TypeError, match="Failed to act"):
795
795
  cirq.act_on(NoActOn()(q).with_tags("test"), args)
@@ -798,11 +798,11 @@ def test_tagged_act_on():
798
798
 
799
799
 
800
800
  def test_single_qubit_gate_validates_on_each():
801
- class Dummy(cirq.testing.SingleQubitGate):
801
+ class Example(cirq.testing.SingleQubitGate):
802
802
  def matrix(self):
803
803
  pass
804
804
 
805
- g = Dummy()
805
+ g = Example()
806
806
  assert g.num_qubits() == 1
807
807
 
808
808
  test_qubits = [cirq.NamedQubit(str(i)) for i in range(3)]
@@ -206,11 +206,13 @@ class CCZPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
206
206
  """
207
207
  if num_controls == 0:
208
208
  return self
209
+ sub_gate: 'cirq.Gate' = self
210
+ if self._global_shift == 0:
211
+ sub_gate = controlled_gate.ControlledGate(
212
+ common_gates.ZPowGate(exponent=self._exponent), num_controls=2
213
+ )
209
214
  return controlled_gate.ControlledGate(
210
- controlled_gate.ControlledGate(
211
- common_gates.ZPowGate(exponent=self._exponent, global_shift=self._global_shift),
212
- num_controls=2,
213
- ),
215
+ sub_gate,
214
216
  num_controls=num_controls,
215
217
  control_values=control_values,
216
218
  control_qid_shape=control_qid_shape,
@@ -518,11 +520,13 @@ class CCXPowGate(gate_features.InterchangeableQubitsGate, eigen_gate.EigenGate):
518
520
  """
519
521
  if num_controls == 0:
520
522
  return self
523
+ sub_gate: 'cirq.Gate' = self
524
+ if self._global_shift == 0:
525
+ sub_gate = controlled_gate.ControlledGate(
526
+ common_gates.XPowGate(exponent=self._exponent), num_controls=2
527
+ )
521
528
  return controlled_gate.ControlledGate(
522
- controlled_gate.ControlledGate(
523
- common_gates.XPowGate(exponent=self._exponent, global_shift=self._global_shift),
524
- num_controls=2,
525
- ),
529
+ sub_gate,
526
530
  num_controls=num_controls,
527
531
  control_values=control_values,
528
532
  control_qid_shape=control_qid_shape,