cirq-core 1.1.0.dev20221220224914__py3-none-any.whl → 1.2.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 (228) hide show
  1. cirq/__init__.py +8 -0
  2. cirq/_compat.py +29 -4
  3. cirq/_compat_test.py +24 -26
  4. cirq/_version.py +32 -1
  5. cirq/_version_test.py +1 -1
  6. cirq/circuits/_block_diagram_drawer_test.py +4 -3
  7. cirq/circuits/circuit.py +109 -63
  8. cirq/circuits/circuit_operation.py +2 -3
  9. cirq/circuits/circuit_operation_test.py +4 -4
  10. cirq/circuits/circuit_test.py +11 -0
  11. cirq/circuits/frozen_circuit.py +13 -1
  12. cirq/circuits/frozen_circuit_test.py +5 -1
  13. cirq/circuits/moment.py +39 -14
  14. cirq/circuits/moment_test.py +7 -0
  15. cirq/circuits/text_diagram_drawer.py +1 -1
  16. cirq/circuits/text_diagram_drawer_test.py +3 -7
  17. cirq/contrib/acquaintance/bipartite.py +1 -1
  18. cirq/contrib/acquaintance/devices.py +2 -2
  19. cirq/contrib/acquaintance/executor.py +5 -2
  20. cirq/contrib/acquaintance/gates.py +3 -2
  21. cirq/contrib/acquaintance/permutation.py +13 -2
  22. cirq/contrib/acquaintance/testing.py +3 -5
  23. cirq/contrib/paulistring/recombine.py +3 -6
  24. cirq/contrib/qasm_import/_parser.py +17 -21
  25. cirq/contrib/qasm_import/_parser_test.py +30 -45
  26. cirq/contrib/qcircuit/qcircuit_test.py +3 -7
  27. cirq/contrib/quantum_volume/quantum_volume.py +3 -3
  28. cirq/contrib/quimb/mps_simulator.py +1 -1
  29. cirq/contrib/quimb/state_vector.py +2 -0
  30. cirq/contrib/quirk/quirk_gate.py +1 -0
  31. cirq/contrib/svg/svg.py +4 -7
  32. cirq/contrib/svg/svg_test.py +29 -1
  33. cirq/devices/grid_qubit.py +26 -28
  34. cirq/devices/grid_qubit_test.py +21 -5
  35. cirq/devices/line_qubit.py +10 -12
  36. cirq/devices/line_qubit_test.py +9 -2
  37. cirq/devices/named_topologies.py +1 -1
  38. cirq/devices/noise_model.py +4 -1
  39. cirq/devices/superconducting_qubits_noise_properties.py +1 -3
  40. cirq/experiments/n_qubit_tomography.py +1 -1
  41. cirq/experiments/qubit_characterizations.py +2 -2
  42. cirq/experiments/single_qubit_readout_calibration.py +1 -1
  43. cirq/experiments/t2_decay_experiment.py +1 -1
  44. cirq/experiments/xeb_simulation_test.py +2 -2
  45. cirq/interop/quirk/cells/testing.py +1 -1
  46. cirq/json_resolver_cache.py +1 -0
  47. cirq/linalg/__init__.py +2 -0
  48. cirq/linalg/decompositions_test.py +4 -4
  49. cirq/linalg/diagonalize_test.py +5 -6
  50. cirq/linalg/transformations.py +72 -9
  51. cirq/linalg/transformations_test.py +23 -7
  52. cirq/ops/__init__.py +4 -0
  53. cirq/ops/arithmetic_operation.py +4 -6
  54. cirq/ops/classically_controlled_operation.py +10 -3
  55. cirq/ops/clifford_gate.py +1 -7
  56. cirq/ops/common_channels.py +21 -15
  57. cirq/ops/common_gate_families.py +2 -3
  58. cirq/ops/common_gates.py +48 -11
  59. cirq/ops/common_gates_test.py +4 -0
  60. cirq/ops/controlled_gate.py +44 -18
  61. cirq/ops/controlled_operation.py +13 -5
  62. cirq/ops/dense_pauli_string.py +14 -19
  63. cirq/ops/diagonal_gate.py +3 -4
  64. cirq/ops/eigen_gate.py +8 -10
  65. cirq/ops/eigen_gate_test.py +6 -0
  66. cirq/ops/gate_operation.py +11 -6
  67. cirq/ops/gate_operation_test.py +11 -2
  68. cirq/ops/gateset.py +2 -1
  69. cirq/ops/gateset_test.py +38 -5
  70. cirq/ops/global_phase_op.py +28 -2
  71. cirq/ops/global_phase_op_test.py +21 -0
  72. cirq/ops/identity.py +1 -1
  73. cirq/ops/kraus_channel_test.py +2 -2
  74. cirq/ops/linear_combinations.py +7 -6
  75. cirq/ops/linear_combinations_test.py +26 -10
  76. cirq/ops/matrix_gates.py +8 -4
  77. cirq/ops/matrix_gates_test.py +25 -3
  78. cirq/ops/measure_util.py +13 -5
  79. cirq/ops/measure_util_test.py +8 -2
  80. cirq/ops/measurement_gate.py +1 -1
  81. cirq/ops/measurement_gate_test.py +9 -4
  82. cirq/ops/mixed_unitary_channel_test.py +4 -4
  83. cirq/ops/named_qubit.py +2 -4
  84. cirq/ops/parity_gates.py +5 -1
  85. cirq/ops/parity_gates_test.py +6 -0
  86. cirq/ops/pauli_gates.py +9 -9
  87. cirq/ops/pauli_string.py +4 -2
  88. cirq/ops/pauli_string_raw_types.py +4 -11
  89. cirq/ops/pauli_string_test.py +13 -13
  90. cirq/ops/pauli_sum_exponential.py +6 -1
  91. cirq/ops/qubit_manager.py +97 -0
  92. cirq/ops/qubit_manager_test.py +66 -0
  93. cirq/ops/raw_types.py +75 -33
  94. cirq/ops/raw_types_test.py +34 -0
  95. cirq/ops/three_qubit_gates.py +16 -10
  96. cirq/ops/three_qubit_gates_test.py +4 -2
  97. cirq/ops/two_qubit_diagonal_gate.py +3 -3
  98. cirq/ops/wait_gate.py +1 -1
  99. cirq/protocols/__init__.py +1 -0
  100. cirq/protocols/act_on_protocol.py +3 -3
  101. cirq/protocols/act_on_protocol_test.py +5 -5
  102. cirq/protocols/apply_channel_protocol.py +9 -8
  103. cirq/protocols/apply_mixture_protocol.py +8 -8
  104. cirq/protocols/apply_mixture_protocol_test.py +1 -1
  105. cirq/protocols/apply_unitary_protocol.py +66 -19
  106. cirq/protocols/apply_unitary_protocol_test.py +50 -0
  107. cirq/protocols/circuit_diagram_info_protocol.py +7 -9
  108. cirq/protocols/decompose_protocol.py +167 -125
  109. cirq/protocols/decompose_protocol_test.py +132 -2
  110. cirq/protocols/has_stabilizer_effect_protocol.py +2 -1
  111. cirq/protocols/inverse_protocol.py +2 -2
  112. cirq/protocols/json_serialization_test.py +3 -3
  113. cirq/protocols/json_test_data/Linspace.json +20 -7
  114. cirq/protocols/json_test_data/Linspace.repr +4 -1
  115. cirq/protocols/json_test_data/Points.json +19 -8
  116. cirq/protocols/json_test_data/Points.repr +4 -1
  117. cirq/protocols/json_test_data/Result.repr_inward +1 -1
  118. cirq/protocols/json_test_data/ResultDict.repr +1 -1
  119. cirq/protocols/json_test_data/ResultDict.repr_inward +1 -1
  120. cirq/protocols/json_test_data/TrialResult.repr_inward +1 -1
  121. cirq/protocols/json_test_data/XPowGate.json +13 -5
  122. cirq/protocols/json_test_data/XPowGate.repr +1 -1
  123. cirq/protocols/json_test_data/ZPowGate.json +13 -5
  124. cirq/protocols/json_test_data/ZPowGate.repr +1 -1
  125. cirq/protocols/json_test_data/ZipLongest.json +19 -0
  126. cirq/protocols/json_test_data/ZipLongest.repr +1 -0
  127. cirq/protocols/json_test_data/spec.py +1 -0
  128. cirq/protocols/kraus_protocol.py +3 -4
  129. cirq/protocols/measurement_key_protocol.py +3 -1
  130. cirq/protocols/mixture_protocol.py +3 -2
  131. cirq/protocols/phase_protocol.py +3 -3
  132. cirq/protocols/pow_protocol.py +1 -2
  133. cirq/protocols/qasm.py +4 -4
  134. cirq/protocols/qid_shape_protocol.py +8 -8
  135. cirq/protocols/resolve_parameters.py +8 -3
  136. cirq/protocols/resolve_parameters_test.py +3 -3
  137. cirq/protocols/unitary_protocol.py +19 -11
  138. cirq/protocols/unitary_protocol_test.py +37 -0
  139. cirq/qis/channels.py +1 -1
  140. cirq/qis/clifford_tableau.py +4 -5
  141. cirq/qis/quantum_state_representation.py +7 -9
  142. cirq/qis/states.py +21 -13
  143. cirq/qis/states_test.py +7 -0
  144. cirq/sim/clifford/clifford_simulator.py +3 -3
  145. cirq/sim/density_matrix_simulation_state.py +2 -1
  146. cirq/sim/density_matrix_simulator.py +1 -1
  147. cirq/sim/density_matrix_simulator_test.py +9 -5
  148. cirq/sim/density_matrix_utils.py +7 -32
  149. cirq/sim/mux.py +2 -2
  150. cirq/sim/simulation_state.py +58 -18
  151. cirq/sim/simulation_state_base.py +5 -2
  152. cirq/sim/simulation_state_test.py +121 -9
  153. cirq/sim/simulation_utils.py +59 -0
  154. cirq/sim/simulation_utils_test.py +32 -0
  155. cirq/sim/simulator.py +2 -1
  156. cirq/sim/simulator_base_test.py +3 -3
  157. cirq/sim/sparse_simulator.py +1 -1
  158. cirq/sim/sparse_simulator_test.py +5 -5
  159. cirq/sim/state_vector.py +7 -36
  160. cirq/sim/state_vector_simulation_state.py +18 -1
  161. cirq/sim/state_vector_simulator.py +3 -2
  162. cirq/sim/state_vector_simulator_test.py +24 -2
  163. cirq/sim/state_vector_test.py +46 -15
  164. cirq/study/__init__.py +1 -0
  165. cirq/study/flatten_expressions.py +2 -2
  166. cirq/study/resolver.py +2 -0
  167. cirq/study/resolver_test.py +1 -1
  168. cirq/study/result.py +1 -1
  169. cirq/study/sweeps.py +103 -9
  170. cirq/study/sweeps_test.py +64 -0
  171. cirq/testing/__init__.py +4 -0
  172. cirq/testing/circuit_compare.py +15 -18
  173. cirq/testing/consistent_act_on.py +4 -4
  174. cirq/testing/consistent_controlled_gate_op_test.py +1 -1
  175. cirq/testing/consistent_decomposition.py +11 -2
  176. cirq/testing/consistent_decomposition_test.py +8 -1
  177. cirq/testing/consistent_protocols.py +2 -0
  178. cirq/testing/consistent_protocols_test.py +8 -4
  179. cirq/testing/consistent_qasm.py +8 -15
  180. cirq/testing/consistent_specified_has_unitary.py +1 -1
  181. cirq/testing/consistent_unitary.py +85 -0
  182. cirq/testing/consistent_unitary_test.py +96 -0
  183. cirq/testing/equivalent_repr_eval.py +10 -10
  184. cirq/testing/json.py +3 -3
  185. cirq/testing/logs.py +1 -1
  186. cirq/testing/order_tester.py +4 -5
  187. cirq/testing/random_circuit.py +3 -5
  188. cirq/testing/sample_gates.py +79 -0
  189. cirq/testing/sample_gates_test.py +59 -0
  190. cirq/transformers/__init__.py +2 -0
  191. cirq/transformers/analytical_decompositions/__init__.py +8 -0
  192. cirq/transformers/analytical_decompositions/pauli_string_decomposition.py +130 -0
  193. cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py +58 -0
  194. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +230 -0
  195. cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +112 -0
  196. cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +1 -3
  197. cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +1 -1
  198. cirq/transformers/expand_composite.py +1 -1
  199. cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +4 -4
  200. cirq/transformers/measurement_transformers.py +4 -4
  201. cirq/transformers/merge_single_qubit_gates.py +17 -4
  202. cirq/transformers/routing/route_circuit_cqc.py +2 -2
  203. cirq/transformers/stratify.py +125 -62
  204. cirq/transformers/stratify_test.py +20 -16
  205. cirq/transformers/transformer_api.py +1 -1
  206. cirq/transformers/transformer_primitives.py +3 -2
  207. cirq/transformers/transformer_primitives_test.py +11 -0
  208. cirq/value/abc_alt.py +3 -2
  209. cirq/value/abc_alt_test.py +1 -0
  210. cirq/value/classical_data.py +10 -10
  211. cirq/value/digits.py +2 -2
  212. cirq/value/linear_dict.py +18 -19
  213. cirq/value/product_state.py +7 -6
  214. cirq/value/value_equality_attr.py +2 -2
  215. cirq/vis/heatmap.py +1 -1
  216. cirq/vis/heatmap_test.py +2 -2
  217. cirq/work/collector.py +2 -2
  218. cirq/work/observable_measurement_data.py +5 -5
  219. cirq/work/observable_readout_calibration.py +3 -1
  220. cirq/work/observable_settings.py +1 -1
  221. cirq/work/pauli_sum_collector.py +9 -8
  222. cirq/work/sampler.py +2 -0
  223. cirq/work/zeros_sampler.py +2 -2
  224. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/METADATA +7 -15
  225. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/RECORD +228 -214
  226. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/WHEEL +1 -1
  227. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/LICENSE +0 -0
  228. {cirq_core-1.1.0.dev20221220224914.dist-info → cirq_core-1.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,112 @@
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
+ from cirq.ops import common_gates
17
+ from cirq.transformers.analytical_decompositions.quantum_shannon_decomposition import (
18
+ _multiplexed_cossin,
19
+ _nth_gray,
20
+ _msb_demuxer,
21
+ _single_qubit_decomposition,
22
+ quantum_shannon_decomposition,
23
+ )
24
+
25
+ import pytest
26
+ import numpy as np
27
+ from scipy.stats import unitary_group
28
+
29
+
30
+ @pytest.mark.parametrize('n_qubits', list(range(1, 8)))
31
+ def test_random_qsd_n_qubit(n_qubits):
32
+ U = unitary_group.rvs(2**n_qubits)
33
+ qubits = [cirq.NamedQubit(f'q{i}') for i in range(n_qubits)]
34
+ circuit = cirq.Circuit(quantum_shannon_decomposition(qubits, U))
35
+ # Test return is equal to inital unitary
36
+ assert cirq.approx_eq(U, circuit.unitary(), atol=1e-9)
37
+ # Test all operations in gate set
38
+ gates = (common_gates.Rz, common_gates.Ry, common_gates.ZPowGate, common_gates.CXPowGate)
39
+ assert all(isinstance(op.gate, gates) for op in circuit.all_operations())
40
+
41
+
42
+ def test_qsd_n_qubit_errors():
43
+ qubits = [cirq.NamedQubit(f'q{i}') for i in range(3)]
44
+ with pytest.raises(ValueError, match="shaped numpy array"):
45
+ cirq.Circuit(quantum_shannon_decomposition(qubits, np.eye(9)))
46
+ with pytest.raises(ValueError, match="is_unitary"):
47
+ cirq.Circuit(quantum_shannon_decomposition(qubits, np.ones((8, 8))))
48
+
49
+
50
+ def test_random_single_qubit_decomposition():
51
+ U = unitary_group.rvs(2)
52
+ qubit = cirq.NamedQubit('q0')
53
+ circuit = cirq.Circuit(_single_qubit_decomposition(qubit, U))
54
+ # Test return is equal to inital unitary
55
+ assert cirq.approx_eq(U, circuit.unitary(), atol=1e-9)
56
+ # Test all operations in gate set
57
+ gates = (common_gates.Rz, common_gates.Ry, common_gates.ZPowGate, common_gates.CXPowGate)
58
+ assert all(isinstance(op.gate, gates) for op in circuit.all_operations())
59
+
60
+
61
+ def test_msb_demuxer():
62
+ U1 = unitary_group.rvs(4)
63
+ U2 = unitary_group.rvs(4)
64
+ U_full = np.kron([[1, 0], [0, 0]], U1) + np.kron([[0, 0], [0, 1]], U2)
65
+ qubits = [cirq.NamedQubit(f'q{i}') for i in range(3)]
66
+ circuit = cirq.Circuit(_msb_demuxer(qubits, U1, U2))
67
+ # Test return is equal to inital unitary
68
+ assert cirq.approx_eq(U_full, circuit.unitary(), atol=1e-9)
69
+ # Test all operations in gate set
70
+ gates = (common_gates.Rz, common_gates.Ry, common_gates.ZPowGate, common_gates.CXPowGate)
71
+ assert all(isinstance(op.gate, gates) for op in circuit.all_operations())
72
+
73
+
74
+ def test_multiplexed_cossin():
75
+ angle_1 = np.random.random_sample() * 2 * np.pi
76
+ angle_2 = np.random.random_sample() * 2 * np.pi
77
+ c1, s1 = np.cos(angle_1), np.sin(angle_1)
78
+ c2, s2 = np.cos(angle_2), np.sin(angle_2)
79
+ multiplexed_ry = [[c1, 0, -s1, 0], [0, c2, 0, -s2], [s1, 0, c1, 0], [0, s2, 0, c2]]
80
+ multiplexed_ry = np.array(multiplexed_ry)
81
+ qubits = [cirq.NamedQubit(f'q{i}') for i in range(2)]
82
+ circuit = cirq.Circuit(_multiplexed_cossin(qubits, [angle_1, angle_2]))
83
+ # Test return is equal to inital unitary
84
+ assert cirq.approx_eq(multiplexed_ry, circuit.unitary(), atol=1e-9)
85
+ # Test all operations in gate set
86
+ gates = (common_gates.Rz, common_gates.Ry, common_gates.ZPowGate, common_gates.CXPowGate)
87
+ assert all(isinstance(op.gate, gates) for op in circuit.all_operations())
88
+
89
+
90
+ @pytest.mark.parametrize(
91
+ 'n, gray',
92
+ [
93
+ (0, 0),
94
+ (1, 1),
95
+ (2, 3),
96
+ (3, 2),
97
+ (4, 6),
98
+ (5, 7),
99
+ (6, 5),
100
+ (7, 4),
101
+ (8, 12),
102
+ (9, 13),
103
+ (10, 15),
104
+ (11, 14),
105
+ (12, 10),
106
+ (13, 11),
107
+ (14, 9),
108
+ (15, 8),
109
+ ],
110
+ )
111
+ def test_nth_gray(n, gray):
112
+ assert _nth_gray(n) == gray
@@ -211,9 +211,7 @@ def test_middle_multiplexor(angles, num_cnots):
211
211
  ]
212
212
  )
213
213
  == num_cnots
214
- ), "expected {} CNOTs got \n {} \n {}".format(
215
- num_cnots, circuit_u1u2_mid, circuit_u1u2_mid.unitary()
216
- )
214
+ ), f"expected {num_cnots} CNOTs got \n {circuit_u1u2_mid} \n {circuit_u1u2_mid.unitary()}"
217
215
 
218
216
 
219
217
  @pytest.mark.parametrize("shift_left", [True, False])
@@ -28,7 +28,7 @@ def decompose_two_qubit_interaction_into_four_fsim_gates(
28
28
  interaction: Union['cirq.SupportsUnitary', np.ndarray],
29
29
  *,
30
30
  fsim_gate: Union['cirq.FSimGate', 'cirq.ISwapPowGate'],
31
- qubits: Sequence['cirq.Qid'] = None,
31
+ qubits: Optional[Sequence['cirq.Qid']] = None,
32
32
  ) -> 'cirq.Circuit':
33
33
  """Decomposes operations into an FSimGate near theta=pi/2, phi=0.
34
34
 
@@ -33,7 +33,7 @@ def expand_composite(
33
33
  """A transformer that expands composite operations via `cirq.decompose`.
34
34
 
35
35
  For each operation in the circuit, this pass examines if the operation can
36
- be decomposed. If it can be, the operation is cleared out and and replaced
36
+ be decomposed. If it can be, the operation is cleared out and replaced
37
37
  with its decomposition using a fixed insertion strategy.
38
38
 
39
39
  Transformation is applied using `cirq.map_operations_and_unroll`, which preserves the
@@ -164,8 +164,8 @@ def kak_vector_infidelity(
164
164
 
165
165
  if ignore_equivalent_vectors:
166
166
  k_diff = k_vec_a - k_vec_b
167
- out = 1 - np.product(np.cos(k_diff), axis=-1) ** 2
168
- out -= np.product(np.sin(k_diff), axis=-1) ** 2
167
+ out = 1 - np.prod(np.cos(k_diff), axis=-1) ** 2
168
+ out -= np.prod(np.sin(k_diff), axis=-1) ** 2
169
169
  return out
170
170
 
171
171
  # We must take the minimum infidelity over all possible locally equivalent
@@ -181,8 +181,8 @@ def kak_vector_infidelity(
181
181
 
182
182
  k_diff = k_vec_a - k_vec_b
183
183
 
184
- out = 1 - np.product(np.cos(k_diff), axis=-1) ** 2
185
- out -= np.product(np.sin(k_diff), axis=-1) ** 2 # (...,192)
184
+ out = 1 - np.prod(np.cos(k_diff), axis=-1) ** 2
185
+ out -= np.prod(np.sin(k_diff), axis=-1) ** 2 # (...,192)
186
186
 
187
187
  return out.min(axis=-1)
188
188
 
@@ -14,7 +14,7 @@
14
14
 
15
15
  import itertools
16
16
  from collections import defaultdict
17
- from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union
17
+ from typing import Any, cast, Dict, Iterable, List, Optional, Sequence, Tuple, TYPE_CHECKING, Union
18
18
 
19
19
  import numpy as np
20
20
 
@@ -420,13 +420,13 @@ class _ConfusionChannel(ops.Gate):
420
420
  return self._kraus
421
421
 
422
422
  def _apply_channel_(self, args: 'cirq.ApplyChannelArgs'):
423
- configs = []
423
+ configs: List[transformations._BuildFromSlicesArgs] = []
424
424
  for i in range(np.prod(self._shape) ** 2):
425
- scale = self._confusion_map.flat[i]
425
+ scale = cast(complex, self._confusion_map.flat[i])
426
426
  if scale == 0:
427
427
  continue
428
428
  index: Any = np.unravel_index(i, self._shape * 2)
429
- slices = []
429
+ slices: List[transformations._SliceConfig] = []
430
430
  axis_count = len(args.left_axes)
431
431
  for j in range(axis_count):
432
432
  s1 = transformations._SliceConfig(
@@ -127,10 +127,23 @@ def merge_single_qubit_moments_to_phxz(
127
127
  return None
128
128
  ret_ops = []
129
129
  for q in m1.qubits | m2.qubits:
130
- mat = protocols.unitary(circuits.Circuit(m.operation_at(q) or [] for m in [m1, m2]))
131
- gate = single_qubit_decompositions.single_qubit_matrix_to_phxz(mat, atol)
132
- if gate:
133
- ret_ops.append(gate(q))
130
+ op1, op2 = m1.operation_at(q), m2.operation_at(q)
131
+ if op1 and op2:
132
+ mat = protocols.unitary(op2) @ protocols.unitary(op1)
133
+ gate = single_qubit_decompositions.single_qubit_matrix_to_phxz(mat, atol)
134
+ if gate:
135
+ ret_ops.append(gate(q))
136
+ else:
137
+ op = op1 or op2
138
+ assert op is not None
139
+ if isinstance(op.gate, ops.PhasedXZGate):
140
+ ret_ops.append(op)
141
+ else:
142
+ gate = single_qubit_decompositions.single_qubit_matrix_to_phxz(
143
+ protocols.unitary(op), atol
144
+ )
145
+ if gate:
146
+ ret_ops.append(gate(q))
134
147
  return circuits.Moment(ret_ops)
135
148
 
136
149
  return transformer_primitives.merge_moments(
@@ -129,7 +129,7 @@ class RouteCQC:
129
129
  context: transformer context storing common configurable options for transformers.
130
130
 
131
131
  Returns:
132
- The routed circuit, which is equivalent to original circuit upto a final qubit
132
+ The routed circuit, which is equivalent to original circuit up to a final qubit
133
133
  permutation and where each 2-qubit operation is between adjacent qubits in the
134
134
  `device_graph`.
135
135
 
@@ -183,7 +183,7 @@ class RouteCQC:
183
183
  context: transformer context storing common configurable options for transformers.
184
184
 
185
185
  Returns:
186
- The routed circuit, which is equivalent to original circuit upto a final qubit
186
+ The routed circuit, which is equivalent to original circuit up to a final qubit
187
187
  permutation and where each 2-qubit operation is between adjacent qubits in the
188
188
  `device_graph`.
189
189
  The initial mapping from logical to physical qubits used as part of the routing
@@ -15,10 +15,10 @@
15
15
  """Transformer pass to repack circuits avoiding simultaneous operations with different classes."""
16
16
 
17
17
  import itertools
18
- from typing import TYPE_CHECKING, Type, Callable, Optional, Union, Iterable, Sequence, List, Tuple
18
+ from typing import TYPE_CHECKING, Type, Callable, Dict, Optional, Union, Iterable, Sequence, List
19
19
 
20
- from cirq import ops, circuits, _import
21
- from cirq.transformers import transformer_api, transformer_primitives
20
+ from cirq import ops, circuits, protocols, _import
21
+ from cirq.transformers import transformer_api
22
22
 
23
23
  drop_empty_moments = _import.LazyLoader('drop_empty_moments', globals(), 'cirq.transformers')
24
24
 
@@ -61,38 +61,36 @@ def stratified_circuit(
61
61
  Returns:
62
62
  A copy of the original circuit, but with re-arranged operations.
63
63
  """
64
-
65
64
  # Normalize categories into classifier functions.
66
- classifiers = [_category_to_classifier(category) for category in categories]
67
- # Make the classifiers exhaustive by adding an "everything else" bucket.
68
- and_the_rest = lambda op: all(not classifier(op) for classifier in classifiers)
69
- classifiers_and_the_rest = [*classifiers, and_the_rest]
65
+ classifiers = _get_classifiers(circuit, categories)
70
66
 
71
67
  # Try the algorithm with each permutation of the classifiers.
72
- classifiers_permutations = list(itertools.permutations(classifiers_and_the_rest))
68
+ smallest_depth = protocols.num_qubits(circuit) * len(circuit) + 1
69
+ shortest_stratified_circuit = circuits.Circuit()
73
70
  reversed_circuit = circuit[::-1]
74
- solutions = []
75
- for c in classifiers_permutations:
76
- solutions.append(
77
- _stratify_circuit(
78
- circuit,
79
- classifiers=list(c),
80
- context=context or transformer_api.TransformerContext(),
81
- )
71
+ for ordered_classifiers in itertools.permutations(classifiers):
72
+ solution = _stratify_circuit(
73
+ circuit,
74
+ classifiers=ordered_classifiers,
75
+ context=context or transformer_api.TransformerContext(),
82
76
  )
77
+ if len(solution) < smallest_depth:
78
+ shortest_stratified_circuit = solution
79
+ smallest_depth = len(solution)
80
+
83
81
  # Do the same thing, except this time in reverse. This helps for some
84
82
  # circuits because it inserts operations at the end instead of at the
85
83
  # beginning.
86
- solutions.append(
87
- _stratify_circuit(
88
- reversed_circuit,
89
- classifiers=list(c),
90
- context=context or transformer_api.TransformerContext(),
91
- )[::-1]
92
- )
84
+ solution = _stratify_circuit(
85
+ reversed_circuit,
86
+ classifiers=ordered_classifiers,
87
+ context=context or transformer_api.TransformerContext(),
88
+ )[::-1]
89
+ if len(solution) < smallest_depth:
90
+ shortest_stratified_circuit = solution
91
+ smallest_depth = len(solution)
93
92
 
94
- # Return the shortest circuit.
95
- return min(solutions, key=lambda c: len(c))
93
+ return shortest_stratified_circuit
96
94
 
97
95
 
98
96
  def _stratify_circuit(
@@ -116,43 +114,88 @@ def _stratify_circuit(
116
114
  Returns:
117
115
  The stratified circuit.
118
116
  """
119
- num_categories = len(classifiers) + 1
120
-
121
- def map_func(m: 'cirq.Moment', _) -> Sequence['cirq.Moment']:
122
- stratified_ops: List[List['cirq.Operation']] = [[] for _ in range(num_categories)]
123
- for op in m:
124
- if set(op.tags) & set(context.tags_to_ignore):
125
- stratified_ops[0].append(op)
126
- continue
127
- for i, classifier in enumerate(classifiers):
128
- if classifier(op):
129
- stratified_ops[i + 1].append(op)
130
- break
131
- return [circuits.Moment(op_list) for op_list in stratified_ops]
132
-
133
- stratified_circuit = transformer_primitives.map_moments(circuit, map_func).unfreeze(copy=False)
134
- assert len(stratified_circuit) == len(circuit) * num_categories
135
-
136
- # Try to move operations to the left to reduce circuit depth, preserving stratification.
137
- for curr_idx, moment in enumerate(stratified_circuit):
138
- curr_category = curr_idx % num_categories
139
- if curr_category == 0:
140
- # Moment containing tagged operations to be ignored.
141
- continue
142
- batch_removals: List[Tuple[int, 'cirq.Operation']] = []
143
- batch_inserts: List[Tuple[int, 'cirq.Operation']] = []
117
+ num_classes = len(classifiers) + 1 # include one "extra" category for ignored operations
118
+ new_moments: List[List['cirq.Operation']] = []
119
+
120
+ # Keep track of the latest time index for each qubit, measurement key, and control key.
121
+ qubit_time_index: Dict['cirq.Qid', int] = {}
122
+ measurement_time_index: Dict['cirq.MeasurementKey', int] = {}
123
+ control_time_index: Dict['cirq.MeasurementKey', int] = {}
124
+
125
+ # The minimum time index for operations with a tag in context.tags_to_ignore.
126
+ last_ignored_ops_time_index = 0
127
+
128
+ for moment in circuit:
129
+ # Identify the new time indices that operations should be moved into.
130
+ ignored_ops = []
131
+ op_time_indices = {}
144
132
  for op in moment:
145
- prv_idx = stratified_circuit.earliest_available_moment(op, end_moment_index=curr_idx)
146
- prv_category = prv_idx % num_categories
147
- should_move_to_next_batch = curr_category < prv_category
148
- prv_idx += curr_category - prv_category + num_categories * should_move_to_next_batch
149
- assert prv_idx <= curr_idx and prv_idx % num_categories == curr_idx % num_categories
150
- if prv_idx < curr_idx:
151
- batch_inserts.append((prv_idx, op))
152
- batch_removals.append((curr_idx, op))
153
- stratified_circuit.batch_remove(batch_removals)
154
- stratified_circuit.batch_insert_into(batch_inserts)
155
- return drop_empty_moments.drop_empty_moments(stratified_circuit)
133
+
134
+ # Identify the earliest moment that can accommodate this op.
135
+ min_time_index_for_op = circuits.circuit.get_earliest_accommodating_moment_index(
136
+ op, qubit_time_index, measurement_time_index, control_time_index
137
+ )
138
+
139
+ # Identify the "class" of this operation (by index).
140
+ ignored_op = any(tag in op.tags for tag in context.tags_to_ignore)
141
+ if not ignored_op:
142
+ op_class = _get_op_class(op, classifiers)
143
+ else:
144
+ op_class = len(classifiers)
145
+ ignored_ops.append(op)
146
+ min_time_index_for_op = max(min_time_index_for_op, last_ignored_ops_time_index + 1)
147
+
148
+ # Identify the time index to place this operation into.
149
+ time_index = (min_time_index_for_op // num_classes) * num_classes + op_class
150
+ if time_index < min_time_index_for_op:
151
+ time_index += num_classes
152
+ op_time_indices[op] = time_index
153
+
154
+ # Assign ignored operations to the same moment.
155
+ if ignored_ops:
156
+ last_ignored_ops_time_index = max(op_time_indices[op] for op in ignored_ops)
157
+ for op in ignored_ops:
158
+ op_time_indices[op] = last_ignored_ops_time_index
159
+
160
+ # Move the operations into their assigned moments.
161
+ for op, time_index in op_time_indices.items():
162
+ if time_index >= len(new_moments):
163
+ new_moments += [[] for _ in range(num_classes)]
164
+ new_moments[time_index].append(op)
165
+
166
+ # Update qubit, measurment key, and control key moments.
167
+ for qubit in op.qubits:
168
+ qubit_time_index[qubit] = time_index
169
+ for key in protocols.measurement_key_objs(op):
170
+ measurement_time_index[key] = time_index
171
+ for key in protocols.control_keys(op):
172
+ control_time_index[key] = time_index
173
+
174
+ return circuits.Circuit(circuits.Moment(moment) for moment in new_moments if moment)
175
+
176
+
177
+ def _get_classifiers(
178
+ circuit: circuits.AbstractCircuit, categories: Iterable[Category]
179
+ ) -> List[Classifier]:
180
+ """Convert a collection of categories into a list of classifiers.
181
+
182
+ The returned list of classifiers is:
183
+ - Exhaustive, meaning every operation in the circuit is classified by at least one classifier.
184
+ - Minimal, meaning unused classifiers are forgotten.
185
+ """
186
+ # Convert all categories into classifiers, and make the list exhaustive by adding a dummy
187
+ # classifier for otherwise unclassified ops.
188
+ classifiers = [_category_to_classifier(cat) for cat in categories] + [_dummy_classifier]
189
+
190
+ # Figure out which classes are actually used in the circuit.
191
+ class_is_used = [False for _ in classifiers]
192
+ for op in circuit.all_operations():
193
+ class_is_used[_get_op_class(op, classifiers)] = True
194
+ if all(class_is_used):
195
+ break
196
+
197
+ # Return only the classifiers that are used.
198
+ return [classifier for classifier, is_used in zip(classifiers, class_is_used) if is_used]
156
199
 
157
200
 
158
201
  # No type for `category` because mypy does not keep the return type when
@@ -177,3 +220,23 @@ def _category_to_classifier(category) -> Classifier:
177
220
  f'Type[cirq.Gate], Type[cirq.Operation], '
178
221
  f'or Callable[[cirq.Operation], bool].'
179
222
  )
223
+
224
+
225
+ def _dummy_classifier(op: 'cirq.Operation') -> bool:
226
+ """Dummy classifier, used to "complete" a collection of classifiers and make it exhaustive."""
227
+ return False # coverage: ignore
228
+
229
+
230
+ def _get_op_class(op: 'cirq.Operation', classifiers: Sequence[Classifier]) -> int:
231
+ """Get the "class" of an operator, by index."""
232
+ for class_index, classifier in enumerate(classifiers):
233
+ if classifier is _dummy_classifier:
234
+ dummy_classifier_index = class_index
235
+ elif classifier(op):
236
+ return class_index
237
+ # If we got this far, the operation did not match any "actual" classifier,
238
+ # so return the index of the dummy classifer.
239
+ try:
240
+ return dummy_classifier_index
241
+ except NameError:
242
+ raise ValueError(f"Operation {op} not identified by any classifier")
@@ -224,17 +224,14 @@ def test_stratify_respects_no_compile_operations():
224
224
  )
225
225
  expected = cirq.Circuit(
226
226
  [
227
+ cirq.Moment(cirq.Z(cirq.LineQubit(4))),
228
+ cirq.Moment(cirq.ISWAP(cirq.LineQubit(3), cirq.LineQubit(4))),
227
229
  cirq.Moment(
228
230
  cirq.TaggedOperation(cirq.X(cirq.LineQubit(0)), 'nocompile'),
229
231
  cirq.TaggedOperation(cirq.ISWAP(cirq.LineQubit(1), cirq.LineQubit(2)), 'nocompile'),
230
232
  ),
231
- cirq.Moment(cirq.X(cirq.LineQubit(0))),
232
- cirq.Moment(cirq.Z(cirq.LineQubit(4))),
233
- cirq.Moment(
234
- cirq.ISWAP(cirq.LineQubit(3), cirq.LineQubit(4)),
235
- cirq.ISWAP(cirq.LineQubit(0), cirq.LineQubit(1)),
236
- ),
237
- cirq.Moment(cirq.X(cirq.LineQubit(3))),
233
+ cirq.Moment(cirq.X(cirq.LineQubit(0)), cirq.X(cirq.LineQubit(3))),
234
+ cirq.Moment(cirq.ISWAP(cirq.LineQubit(0), cirq.LineQubit(1))),
238
235
  ]
239
236
  )
240
237
  cirq.testing.assert_has_diagram(
@@ -254,15 +251,15 @@ def test_stratify_respects_no_compile_operations():
254
251
  cirq.testing.assert_has_diagram(
255
252
  expected,
256
253
  '''
257
- 0: ───X['nocompile']───────X───────iSwap───────
258
-
259
- 1: ───iSwap['nocompile']───────────iSwap───────
260
-
261
- 2: ───iSwap────────────────────────────────────
262
-
263
- 3: ────────────────────────────────iSwap───X───
264
-
265
- 4: ────────────────────────────Z───iSwap───────
254
+ 0: ───────────────X['nocompile']───────X───iSwap───
255
+
256
+ 1: ───────────────iSwap['nocompile']───────iSwap───
257
+
258
+ 2: ───────────────iSwap────────────────────────────
259
+
260
+ 3: ───────iSwap────────────────────────X───────────
261
+
262
+ 4: ───Z───iSwap────────────────────────────────────
266
263
  ''',
267
264
  )
268
265
  cirq.testing.assert_same_circuits(
@@ -409,3 +406,10 @@ def test_surface_code_cycle_stratifies_without_growing():
409
406
  # https://github.com/quantumlib/Cirq/pull/2772/ for some discussion on
410
407
  # this, as well as a more optimal but much more complex and slow solution.
411
408
  assert len(stratified) == 9
409
+
410
+
411
+ def test_unclassified_ops():
412
+ op = cirq.X(cirq.q(0))
413
+ classifiers = []
414
+ with pytest.raises(ValueError, match='not identified by any classifier'):
415
+ cirq.transformers.stratify._get_op_class(op, classifiers)
@@ -109,7 +109,7 @@ class TransformerLogger:
109
109
  The `LogLevel`s can be used to control the input processing and output resolution of the logs.
110
110
  """
111
111
 
112
- def __init__(self):
112
+ def __init__(self) -> None:
113
113
  """Initializes TransformerLogger."""
114
114
  self._curr_id: int = 0
115
115
  self._logs: List[_LoggerNode] = []
@@ -158,7 +158,8 @@ def map_operations(
158
158
 
159
159
  return map_moments(
160
160
  circuit,
161
- lambda m, i: [circuits.Moment(apply_map(op, i) for op in m.operations)],
161
+ lambda m, i: circuits.Circuit(apply_map(op, i) for op in m.operations).moments
162
+ or [circuits.Moment()],
162
163
  deep=deep,
163
164
  tags_to_ignore=tags_to_ignore,
164
165
  )
@@ -296,7 +297,7 @@ def merge_operations(
296
297
 
297
298
  The method iterates on the input circuit moment-by-moment from left to right and attempts
298
299
  to repeatedly merge each operation in the latest moment with all the corresponding merge-able
299
- operations to it's left.
300
+ operations to its left.
300
301
 
301
302
  If op1 and op2 are merged, both op1 and op2 are deleted from the circuit and
302
303
  the resulting `merged_op` is inserted at the index corresponding to the larger
@@ -414,6 +414,17 @@ def test_map_operations_can_add_qubits_if_flag_false():
414
414
  cirq.testing.assert_same_circuits(c_mapped, cirq.Circuit(cirq.CNOT(q[0], q[1])))
415
415
 
416
416
 
417
+ def test_map_operations_maps_different_ops_from_same_moment_to_shared_qubits():
418
+ q = cirq.LineQubit.range(3)
419
+ c = cirq.Circuit(cirq.H.on_each(q[:2]))
420
+ c_mapped = cirq.map_operations(
421
+ c, lambda op, _: op.controlled_by(q[2]), raise_if_add_qubits=False
422
+ )
423
+ cirq.testing.assert_same_circuits(
424
+ c_mapped, cirq.Circuit(cirq.H(q[0]).controlled_by(q[2]), cirq.H(q[1]).controlled_by(q[2]))
425
+ )
426
+
427
+
417
428
  def test_map_operations_can_drop_operations():
418
429
  q = cirq.LineQubit.range(2)
419
430
  c = cirq.Circuit(cirq.X(q[0]), cirq.Y(q[1]), cirq.X(q[1]), cirq.Y(q[0]))
cirq/value/abc_alt.py CHANGED
@@ -11,6 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
+
14
15
  """A more flexible abstract base class metaclass ABCMetaImplementAnyOneOf."""
15
16
 
16
17
  import abc
@@ -91,9 +92,9 @@ class ABCMetaImplementAnyOneOf(abc.ABCMeta):
91
92
  value = getattr(cls, name)
92
93
  except AttributeError:
93
94
  raise TypeError(
94
- 'A method named \'{}\' was listed as a possible '
95
+ f"A method named '{name}' was listed as a possible "
95
96
  'implementation alternative but it does not exist in the '
96
- 'definition of {!r}.'.format(name, cls)
97
+ f'definition of {cls!r}.'
97
98
  )
98
99
  if getattr(value, '__isabstractmethod__', False):
99
100
  return False
@@ -155,6 +155,7 @@ def test_two_alternatives():
155
155
  @alternative(requires='alt2', implementation=_default_impl2)
156
156
  def my_method(self, arg, kw=99) -> str:
157
157
  """Docstring."""
158
+ raise NotImplementedError
158
159
 
159
160
  @abc.abstractmethod
160
161
  def alt1(self) -> Optional[str]:
@@ -14,7 +14,8 @@
14
14
 
15
15
  import abc
16
16
  import enum
17
- from typing import Dict, List, Mapping, Sequence, Tuple, TYPE_CHECKING, TypeVar
17
+ from typing import Dict, List, Mapping, Optional, Sequence, Tuple, TYPE_CHECKING
18
+ from typing_extensions import Self
18
19
 
19
20
  from cirq.value import digits, value_equality_attr
20
21
 
@@ -38,10 +39,7 @@ class MeasurementType(enum.IntEnum):
38
39
  CHANNEL = 2
39
40
 
40
41
  def __repr__(self):
41
- return f'cirq.{str(self)}'
42
-
43
-
44
- TSelf = TypeVar('TSelf', bound='ClassicalDataStoreReader')
42
+ return f'cirq.MeasurementType.{self.name}'
45
43
 
46
44
 
47
45
  class ClassicalDataStoreReader(abc.ABC):
@@ -101,7 +99,7 @@ class ClassicalDataStoreReader(abc.ABC):
101
99
  """
102
100
 
103
101
  @abc.abstractmethod
104
- def copy(self: TSelf) -> TSelf:
102
+ def copy(self) -> Self:
105
103
  """Creates a copy of the object."""
106
104
 
107
105
 
@@ -142,10 +140,12 @@ class ClassicalDataDictionaryStore(ClassicalDataStore):
142
140
  def __init__(
143
141
  self,
144
142
  *,
145
- _records: Dict['cirq.MeasurementKey', List[Tuple[int, ...]]] = None,
146
- _measured_qubits: Dict['cirq.MeasurementKey', List[Tuple['cirq.Qid', ...]]] = None,
147
- _channel_records: Dict['cirq.MeasurementKey', List[int]] = None,
148
- _measurement_types: Dict['cirq.MeasurementKey', 'cirq.MeasurementType'] = None,
143
+ _records: Optional[Dict['cirq.MeasurementKey', List[Tuple[int, ...]]]] = None,
144
+ _measured_qubits: Optional[
145
+ Dict['cirq.MeasurementKey', List[Tuple['cirq.Qid', ...]]]
146
+ ] = None,
147
+ _channel_records: Optional[Dict['cirq.MeasurementKey', List[int]]] = None,
148
+ _measurement_types: Optional[Dict['cirq.MeasurementKey', 'cirq.MeasurementType']] = None,
149
149
  ):
150
150
  """Initializes a `ClassicalDataDictionaryStore` object."""
151
151
  if not _measurement_types: