cirq-core 1.2.0.dev20230717225858__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.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/METADATA +11 -19
  155. {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/RECORD +158 -150
  156. {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/WHEEL +1 -1
  157. {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/LICENSE +0 -0
  158. {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,250 @@
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
+ def __str__(self):
33
+ return 'TestGateAlloc'
34
+
35
+
36
+ class GateAllocAndBorrowInDecompose(cirq.Gate):
37
+ def __init__(self, num_alloc: int = 1):
38
+ self.num_alloc = num_alloc
39
+
40
+ def _num_qubits_(self) -> int:
41
+ return 1
42
+
43
+ def __str__(self) -> str:
44
+ return 'TestGate'
45
+
46
+ def _decompose_with_context_(self, qubits, context):
47
+ assert context is not None
48
+ qm = context.qubit_manager
49
+ qa, qb = qm.qalloc(self.num_alloc), qm.qborrow(self.num_alloc)
50
+ for q, b in zip(qa, qb):
51
+ yield cirq.CSWAP(qubits[0], q, b)
52
+ yield cirq.qft(*qb).controlled_by(qubits[0])
53
+ for q, b in zip(qa, qb):
54
+ yield cirq.CSWAP(qubits[0], q, b)
55
+ qm.qfree(qa + qb)
56
+
57
+
58
+ def get_decompose_func(gate_type, qm):
59
+ def decompose_func(op: cirq.Operation, _):
60
+ return (
61
+ cirq.decompose_once(op, context=cirq.DecompositionContext(qm))
62
+ if isinstance(op.gate, gate_type)
63
+ else op
64
+ )
65
+
66
+ return decompose_func
67
+
68
+
69
+ def test_map_clean_and_borrowable_qubits_greedy_types():
70
+ qm = cirq.ops.SimpleQubitManager()
71
+ q = cirq.LineQubit.range(2)
72
+ g = GateAllocInDecompose(1)
73
+ circuit = cirq.Circuit(cirq.Moment(g(q[0]), g(q[1])))
74
+ cirq.testing.assert_has_diagram(
75
+ circuit,
76
+ """
77
+ 0: ───TestGateAlloc───
78
+
79
+ 1: ───TestGateAlloc───
80
+ """,
81
+ )
82
+ unrolled_circuit = cirq.map_operations_and_unroll(
83
+ circuit, map_func=get_decompose_func(GateAllocInDecompose, qm), raise_if_add_qubits=False
84
+ )
85
+ cirq.testing.assert_has_diagram(
86
+ unrolled_circuit,
87
+ """
88
+ ┌──┐
89
+ _c(0): ────X─────
90
+
91
+ _c(1): ────┼X────
92
+ ││
93
+ 0: ────────@┼────
94
+
95
+ 1: ─────────@────
96
+ └──┘
97
+ """,
98
+ )
99
+
100
+ # Maximize parallelism by maximizing qubit width and minimizing qubit reuse.
101
+ qubit_manager = cirq.GreedyQubitManager(prefix='ancilla', size=2, maximize_reuse=False)
102
+ allocated_circuit = cirq.map_clean_and_borrowable_qubits(unrolled_circuit, qm=qubit_manager)
103
+ cirq.testing.assert_has_diagram(
104
+ allocated_circuit,
105
+ """
106
+ ┌──┐
107
+ 0: ────────────@─────
108
+
109
+ 1: ────────────┼@────
110
+ ││
111
+ ancilla_0: ────X┼────
112
+
113
+ ancilla_1: ─────X────
114
+ └──┘
115
+ """,
116
+ )
117
+
118
+ # Minimize parallelism by minimizing qubit width and maximizing qubit reuse.
119
+ qubit_manager = cirq.GreedyQubitManager(prefix='ancilla', size=2, maximize_reuse=True)
120
+ allocated_circuit = cirq.map_clean_and_borrowable_qubits(unrolled_circuit, qm=qubit_manager)
121
+ cirq.testing.assert_has_diagram(
122
+ allocated_circuit,
123
+ """
124
+ 0: ───────────@───────
125
+
126
+ 1: ───────────┼───@───
127
+ │ │
128
+ ancilla_1: ───X───X───
129
+ """,
130
+ )
131
+
132
+
133
+ def test_map_clean_and_borrowable_qubits_borrows():
134
+ qm = cirq.ops.SimpleQubitManager()
135
+ op = GateAllocAndBorrowInDecompose(3).on(cirq.NamedQubit("original"))
136
+ extra = cirq.LineQubit.range(3)
137
+ circuit = cirq.Circuit(
138
+ cirq.H.on_each(*extra),
139
+ cirq.Moment(op),
140
+ cirq.decompose_once(op, context=cirq.DecompositionContext(qm)),
141
+ )
142
+ cirq.testing.assert_has_diagram(
143
+ circuit,
144
+ """
145
+ _b(0): ─────────────────────×───────────qft───×───────────
146
+ │ │ │
147
+ _b(1): ─────────────────────┼───×───────#2────┼───×───────
148
+ │ │ │ │ │
149
+ _b(2): ─────────────────────┼───┼───×───#3────┼───┼───×───
150
+ │ │ │ │ │ │ │
151
+ _c(0): ─────────────────────×───┼───┼───┼─────×───┼───┼───
152
+ │ │ │ │ │ │ │
153
+ _c(1): ─────────────────────┼───×───┼───┼─────┼───×───┼───
154
+ │ │ │ │ │ │ │
155
+ _c(2): ─────────────────────┼───┼───×───┼─────┼───┼───×───
156
+ │ │ │ │ │ │ │
157
+ 0: ──────────H──────────────┼───┼───┼───┼─────┼───┼───┼───
158
+ │ │ │ │ │ │ │
159
+ 1: ──────────H──────────────┼───┼───┼───┼─────┼───┼───┼───
160
+ │ │ │ │ │ │ │
161
+ 2: ──────────H──────────────┼───┼───┼───┼─────┼───┼───┼───
162
+ │ │ │ │ │ │ │
163
+ original: ───────TestGate───@───@───@───@─────@───@───@───
164
+ """,
165
+ )
166
+ allocated_circuit = cirq.map_clean_and_borrowable_qubits(circuit)
167
+ cirq.testing.assert_has_diagram(
168
+ allocated_circuit,
169
+ """
170
+ 0: ───────────H──────────×───────────qft───×───────────
171
+ │ │ │
172
+ 1: ───────────H──────────┼───×───────#2────┼───×───────
173
+ │ │ │ │ │
174
+ 2: ───────────H──────────┼───┼───×───#3────┼───┼───×───
175
+ │ │ │ │ │ │ │
176
+ ancilla_0: ──────────────×───┼───┼───┼─────×───┼───┼───
177
+ │ │ │ │ │ │ │
178
+ ancilla_1: ──────────────┼───×───┼───┼─────┼───×───┼───
179
+ │ │ │ │ │ │ │
180
+ ancilla_2: ──────────────┼───┼───×───┼─────┼───┼───×───
181
+ │ │ │ │ │ │ │
182
+ original: ────TestGate───@───@───@───@─────@───@───@───""",
183
+ )
184
+ decompose_func = get_decompose_func(GateAllocAndBorrowInDecompose, qm)
185
+ allocated_and_decomposed_circuit = cirq.map_clean_and_borrowable_qubits(
186
+ cirq.map_operations_and_unroll(circuit, map_func=decompose_func, raise_if_add_qubits=False)
187
+ )
188
+ cirq.testing.assert_has_diagram(
189
+ allocated_and_decomposed_circuit,
190
+ """
191
+ 0: ───────────H───×───────────qft───×───────────×───────────qft───×───────────
192
+ │ │ │ │ │ │
193
+ 1: ───────────H───┼───×───────#2────┼───×───────┼───×───────#2────┼───×───────
194
+ │ │ │ │ │ │ │ │ │ │
195
+ 2: ───────────H───┼───┼───×───#3────┼───┼───×───┼───┼───×───#3────┼───┼───×───
196
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
197
+ ancilla_0: ───────×───┼───┼───┼─────×───┼───┼───×───┼───┼───┼─────×───┼───┼───
198
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
199
+ ancilla_1: ───────┼───×───┼───┼─────┼───×───┼───┼───×───┼───┼─────┼───×───┼───
200
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
201
+ ancilla_2: ───────┼───┼───×───┼─────┼───┼───×───┼───┼───×───┼─────┼───┼───×───
202
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
203
+ original: ────────@───@───@───@─────@───@───@───@───@───@───@─────@───@───@───
204
+ """,
205
+ )
206
+
207
+ # If TestGate is in the first moment then we end up allocating 4 ancilla
208
+ # qubits because there are no available qubits to borrow in the first moment.
209
+ allocated_and_decomposed_circuit = cirq.map_clean_and_borrowable_qubits(
210
+ cirq.map_operations_and_unroll(
211
+ cirq.align_left(circuit), map_func=decompose_func, raise_if_add_qubits=False
212
+ )
213
+ )
214
+ cirq.testing.assert_has_diagram(
215
+ allocated_and_decomposed_circuit,
216
+ """
217
+ 0: ───────────H───×───────#2────────×───────×───────────qft───×───────────
218
+ │ │ │ │ │ │
219
+ 1: ───────────H───┼───×───#3────────┼───×───┼───×───────#2────┼───×───────
220
+ │ │ │ │ │ │ │ │ │ │
221
+ 2: ───────────H───┼───┼───┼─────────┼───┼───┼───┼───×───#3────┼───┼───×───
222
+ │ │ │ │ │ │ │ │ │ │ │ │
223
+ ancilla_0: ───×───┼───┼───┼─────×───┼───┼───┼───×───┼───┼─────┼───×───┼───
224
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
225
+ ancilla_1: ───×───┼───┼───qft───×───┼───┼───×───┼───┼───┼─────×───┼───┼───
226
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
227
+ ancilla_2: ───┼───×───┼───┼─────┼───×───┼───┼───┼───×───┼─────┼───┼───×───
228
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
229
+ ancilla_3: ───┼───┼───×───┼─────┼───┼───×───┼───┼───┼───┼─────┼───┼───┼───
230
+ │ │ │ │ │ │ │ │ │ │ │ │ │ │
231
+ original: ────@───@───@───@─────@───@───@───@───@───@───@─────@───@───@───
232
+ """,
233
+ )
234
+
235
+
236
+ def test_map_clean_and_borrowable_qubits_deallocates_only_once():
237
+ q = [cirq.ops.BorrowableQubit(i) for i in range(2)] + [cirq.q('q')]
238
+ circuit = cirq.Circuit(cirq.X.on_each(*q), cirq.Y(q[1]), cirq.Z(q[1]))
239
+ greedy_mm = cirq.GreedyQubitManager(prefix="a", size=2)
240
+ mapped_circuit = cirq.map_clean_and_borrowable_qubits(circuit, qm=greedy_mm)
241
+ cirq.testing.assert_has_diagram(
242
+ mapped_circuit,
243
+ '''
244
+ a_0: ───X───────────
245
+
246
+ a_1: ───X───Y───Z───
247
+
248
+ q: ─────X───────────
249
+ ''',
250
+ )
@@ -15,7 +15,7 @@
15
15
  """Heuristic qubit routing algorithm based on arxiv:1902.08091."""
16
16
 
17
17
  from typing import Any, Dict, List, Optional, Set, Sequence, Tuple, TYPE_CHECKING
18
- from itertools import combinations
18
+ import itertools
19
19
  import networkx as nx
20
20
 
21
21
  from cirq import circuits, ops, protocols
@@ -48,7 +48,9 @@ def _disjoint_nc2_combinations(
48
48
  Returns:
49
49
  All 2-combinations between qubit pairs that are disjoint.
50
50
  """
51
- return [pair for pair in combinations(qubit_pairs, 2) if set(pair[0]).isdisjoint(pair[1])]
51
+ return [
52
+ pair for pair in itertools.combinations(qubit_pairs, 2) if set(pair[0]).isdisjoint(pair[1])
53
+ ]
52
54
 
53
55
 
54
56
  @transformer_api.transformer
@@ -245,17 +247,34 @@ class RouteCQC:
245
247
  The i'th entry in the nested two-qubit and single-qubit ops correspond to the two-qubit
246
248
  gates and single-qubit gates of the i'th timesteps respectively. When constructing the
247
249
  output routed circuit, single-qubit operations are inserted before two-qubit operations.
250
+
251
+ Raises:
252
+ ValueError: if circuit has intermediate measurements that act on three or more
253
+ qubits with a custom key.
248
254
  """
249
255
  two_qubit_circuit = circuits.Circuit()
250
256
  single_qubit_ops: List[List[cirq.Operation]] = []
251
- for moment in circuit:
257
+
258
+ for i, moment in enumerate(circuit):
252
259
  for op in moment:
253
260
  timestep = two_qubit_circuit.earliest_available_moment(op)
254
261
  single_qubit_ops.extend([] for _ in range(timestep + 1 - len(single_qubit_ops)))
255
262
  two_qubit_circuit.append(
256
263
  circuits.Moment() for _ in range(timestep + 1 - len(two_qubit_circuit))
257
264
  )
258
- if protocols.num_qubits(op) == 2 and not protocols.is_measurement(op):
265
+ if protocols.num_qubits(op) > 2 and protocols.is_measurement(op):
266
+ key = op.gate.key # type: ignore
267
+ default_key = ops.measure(op.qubits).gate.key # type: ignore
268
+ if len(circuit.moments) == i + 1:
269
+ single_qubit_ops[timestep].append(op)
270
+ elif key in ('', default_key):
271
+ single_qubit_ops[timestep].extend(ops.measure(qubit) for qubit in op.qubits)
272
+ else:
273
+ raise ValueError(
274
+ 'Intermediate measurements on three or more qubits '
275
+ 'with a custom key are not supported'
276
+ )
277
+ elif protocols.num_qubits(op) == 2:
259
278
  two_qubit_circuit[timestep] = two_qubit_circuit[timestep].with_operation(op)
260
279
  else:
261
280
  single_qubit_ops[timestep].append(op)
@@ -107,6 +107,48 @@ def test_circuit_with_measurement_gates():
107
107
  cirq.testing.assert_same_circuits(routed_circuit, circuit)
108
108
 
109
109
 
110
+ def test_circuit_with_two_qubit_intermediate_measurement_gate():
111
+ device = cirq.testing.construct_ring_device(2)
112
+ device_graph = device.metadata.nx_graph
113
+ router = cirq.RouteCQC(device_graph)
114
+ qs = cirq.LineQubit.range(2)
115
+ hard_coded_mapper = cirq.HardCodedInitialMapper({qs[i]: qs[i] for i in range(2)})
116
+ circuit = cirq.Circuit([cirq.Moment(cirq.measure(qs)), cirq.Moment(cirq.H.on_each(qs))])
117
+ routed_circuit = router(
118
+ circuit, initial_mapper=hard_coded_mapper, context=cirq.TransformerContext(deep=True)
119
+ )
120
+ device.validate_circuit(routed_circuit)
121
+
122
+
123
+ def test_circuit_with_multi_qubit_intermediate_measurement_gate_and_with_default_key():
124
+ device = cirq.testing.construct_ring_device(3)
125
+ device_graph = device.metadata.nx_graph
126
+ router = cirq.RouteCQC(device_graph)
127
+ qs = cirq.LineQubit.range(3)
128
+ hard_coded_mapper = cirq.HardCodedInitialMapper({qs[i]: qs[i] for i in range(3)})
129
+ circuit = cirq.Circuit([cirq.Moment(cirq.measure(qs)), cirq.Moment(cirq.H.on_each(qs))])
130
+ routed_circuit = router(
131
+ circuit, initial_mapper=hard_coded_mapper, context=cirq.TransformerContext(deep=True)
132
+ )
133
+ expected = cirq.Circuit([cirq.Moment(cirq.measure_each(qs)), cirq.Moment(cirq.H.on_each(qs))])
134
+ cirq.testing.assert_same_circuits(routed_circuit, expected)
135
+
136
+
137
+ def test_circuit_with_multi_qubit_intermediate_measurement_gate_with_custom_key():
138
+ device = cirq.testing.construct_ring_device(3)
139
+ device_graph = device.metadata.nx_graph
140
+ router = cirq.RouteCQC(device_graph)
141
+ qs = cirq.LineQubit.range(3)
142
+ hard_coded_mapper = cirq.HardCodedInitialMapper({qs[i]: qs[i] for i in range(3)})
143
+ circuit = cirq.Circuit(
144
+ [cirq.Moment(cirq.measure(qs, key="test")), cirq.Moment(cirq.H.on_each(qs))]
145
+ )
146
+ with pytest.raises(ValueError):
147
+ _ = router(
148
+ circuit, initial_mapper=hard_coded_mapper, context=cirq.TransformerContext(deep=True)
149
+ )
150
+
151
+
110
152
  def test_circuit_with_non_unitary_and_global_phase():
111
153
  device = cirq.testing.construct_ring_device(4)
112
154
  device_graph = device.metadata.nx_graph
@@ -130,7 +130,6 @@ def _stratify_circuit(
130
130
  ignored_ops = []
131
131
  op_time_indices = {}
132
132
  for op in moment:
133
-
134
133
  # Identify the earliest moment that can accommodate this op.
135
134
  min_time_index_for_op = circuits.circuit.get_earliest_accommodating_moment_index(
136
135
  op, qubit_time_index, measurement_time_index, control_time_index
@@ -183,9 +182,9 @@ def _get_classifiers(
183
182
  - Exhaustive, meaning every operation in the circuit is classified by at least one classifier.
184
183
  - Minimal, meaning unused classifiers are forgotten.
185
184
  """
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]
185
+ # Convert all categories into classifiers, and make the list exhaustive by
186
+ # adding an example classifier for otherwise unclassified ops.
187
+ classifiers = [_category_to_classifier(cat) for cat in categories] + [_mock_classifier]
189
188
 
190
189
  # Figure out which classes are actually used in the circuit.
191
190
  class_is_used = [False for _ in classifiers]
@@ -222,21 +221,21 @@ def _category_to_classifier(category) -> Classifier:
222
221
  )
223
222
 
224
223
 
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
224
+ def _mock_classifier(op: 'cirq.Operation') -> bool:
225
+ """Mock classifier, used to "complete" a collection of classifiers and make it exhaustive."""
226
+ return False # pragma: no cover
228
227
 
229
228
 
230
229
  def _get_op_class(op: 'cirq.Operation', classifiers: Sequence[Classifier]) -> int:
231
230
  """Get the "class" of an operator, by index."""
232
231
  for class_index, classifier in enumerate(classifiers):
233
- if classifier is _dummy_classifier:
234
- dummy_classifier_index = class_index
232
+ if classifier is _mock_classifier:
233
+ mock_classifier_index = class_index
235
234
  elif classifier(op):
236
235
  return class_index
237
236
  # If we got this far, the operation did not match any "actual" classifier,
238
- # so return the index of the dummy classifer.
237
+ # so return the index of the mock classifer.
239
238
  try:
240
- return dummy_classifier_index
239
+ return mock_classifier_index
241
240
  except NameError:
242
241
  raise ValueError(f"Operation {op} not identified by any classifier")
@@ -19,7 +19,7 @@ from cirq.protocols.decompose_protocol import DecomposeResult
19
19
 
20
20
 
21
21
  def test_compilation_target_gateset():
22
- class DummyTargetGateset(cirq.CompilationTargetGateset):
22
+ class ExampleTargetGateset(cirq.CompilationTargetGateset):
23
23
  def __init__(self):
24
24
  super().__init__(cirq.AnyUnitaryGateFamily(2))
25
25
 
@@ -34,7 +34,7 @@ def test_compilation_target_gateset():
34
34
  def preprocess_transformers(self) -> List[cirq.TRANSFORMER]:
35
35
  return []
36
36
 
37
- gateset = DummyTargetGateset()
37
+ gateset = ExampleTargetGateset()
38
38
 
39
39
  q = cirq.LineQubit.range(2)
40
40
  assert cirq.X(q[0]) not in gateset
@@ -57,7 +57,7 @@ def test_compilation_target_gateset():
57
57
  ]
58
58
 
59
59
 
60
- class DummyCXTargetGateset(cirq.TwoQubitCompilationTargetGateset):
60
+ class ExampleCXTargetGateset(cirq.TwoQubitCompilationTargetGateset):
61
61
  def __init__(self):
62
62
  super().__init__(cirq.AnyUnitaryGateFamily(1), cirq.CNOT)
63
63
 
@@ -90,7 +90,7 @@ class DummyCXTargetGateset(cirq.TwoQubitCompilationTargetGateset):
90
90
 
91
91
  def test_two_qubit_compilation_leaves_single_gates_in_gateset():
92
92
  q = cirq.LineQubit.range(2)
93
- gateset = DummyCXTargetGateset()
93
+ gateset = ExampleCXTargetGateset()
94
94
 
95
95
  c = cirq.Circuit(cirq.X(q[0]) ** 0.5)
96
96
  cirq.testing.assert_same_circuits(cirq.optimize_for_target_gateset(c, gateset=gateset), c)
@@ -103,7 +103,7 @@ def test_two_qubit_compilation_merges_runs_of_single_qubit_gates():
103
103
  q = cirq.LineQubit.range(2)
104
104
  c = cirq.Circuit(cirq.CNOT(*q), cirq.X(q[0]), cirq.Y(q[0]), cirq.CNOT(*q))
105
105
  cirq.testing.assert_same_circuits(
106
- cirq.optimize_for_target_gateset(c, gateset=DummyCXTargetGateset()),
106
+ cirq.optimize_for_target_gateset(c, gateset=ExampleCXTargetGateset()),
107
107
  cirq.Circuit(
108
108
  cirq.CNOT(*q),
109
109
  cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0, z_exponent=-1).on(q[0]),
@@ -113,7 +113,7 @@ def test_two_qubit_compilation_merges_runs_of_single_qubit_gates():
113
113
 
114
114
 
115
115
  def test_two_qubit_compilation_decompose_operation_not_implemented():
116
- gateset = DummyCXTargetGateset()
116
+ gateset = ExampleCXTargetGateset()
117
117
  q = cirq.LineQubit.range(3)
118
118
  assert gateset.decompose_to_target_gateset(cirq.measure(q[0]), 1) is NotImplemented
119
119
  assert gateset.decompose_to_target_gateset(cirq.measure(*q[:2]), 1) is NotImplemented
@@ -145,7 +145,7 @@ def test_two_qubit_compilation_merge_and_replace_to_target_gateset():
145
145
  )
146
146
  c_new = cirq.optimize_for_target_gateset(
147
147
  c_orig,
148
- gateset=DummyCXTargetGateset(),
148
+ gateset=ExampleCXTargetGateset(),
149
149
  context=cirq.TransformerContext(tags_to_ignore=("no_compile",)),
150
150
  )
151
151
  cirq.testing.assert_has_diagram(
@@ -187,7 +187,7 @@ m: ═════════════════════════
187
187
  )
188
188
  c_new = cirq.optimize_for_target_gateset(
189
189
  c_orig,
190
- gateset=DummyCXTargetGateset(),
190
+ gateset=ExampleCXTargetGateset(),
191
191
  context=cirq.TransformerContext(tags_to_ignore=("no_compile",)),
192
192
  )
193
193
  cirq.testing.assert_has_diagram(
@@ -203,7 +203,7 @@ m: ═════════════════════════
203
203
 
204
204
 
205
205
  def test_two_qubit_compilation_replaces_only_if_2q_gate_count_is_less():
206
- class DummyTargetGateset(cirq.TwoQubitCompilationTargetGateset):
206
+ class ExampleTargetGateset(cirq.TwoQubitCompilationTargetGateset):
207
207
  def __init__(self):
208
208
  super().__init__(cirq.X, cirq.CNOT)
209
209
 
@@ -218,7 +218,7 @@ def test_two_qubit_compilation_replaces_only_if_2q_gate_count_is_less():
218
218
  ops = [cirq.Y.on_each(*q), cirq.CNOT(*q), cirq.Z.on_each(*q)]
219
219
  c_orig = cirq.Circuit(ops)
220
220
  c_expected = cirq.Circuit(cirq.X.on_each(*q), ops[-2:])
221
- c_new = cirq.optimize_for_target_gateset(c_orig, gateset=DummyTargetGateset())
221
+ c_new = cirq.optimize_for_target_gateset(c_orig, gateset=ExampleTargetGateset())
222
222
  cirq.testing.assert_same_circuits(c_new, c_expected)
223
223
 
224
224
 
@@ -231,8 +231,7 @@ def test_not_decompose_partial_czs():
231
231
 
232
232
 
233
233
  def test_avoids_decompose_when_matrix_available():
234
- class OtherXX(cirq.testing.TwoQubitGate):
235
- # coverage: ignore
234
+ class OtherXX(cirq.testing.TwoQubitGate): # pragma: no cover
236
235
  def _has_unitary_(self) -> bool:
237
236
  return True
238
237
 
@@ -243,8 +242,7 @@ def test_avoids_decompose_when_matrix_available():
243
242
  def _decompose_(self, qubits):
244
243
  assert False
245
244
 
246
- class OtherOtherXX(cirq.testing.TwoQubitGate):
247
- # coverage: ignore
245
+ class OtherOtherXX(cirq.testing.TwoQubitGate): # pragma: no cover
248
246
  def _has_unitary_(self) -> bool:
249
247
  return True
250
248
 
@@ -262,18 +260,18 @@ def test_avoids_decompose_when_matrix_available():
262
260
 
263
261
 
264
262
  def test_composite_gates_without_matrix():
265
- class CompositeDummy(cirq.testing.SingleQubitGate):
263
+ class CompositeExample(cirq.testing.SingleQubitGate):
266
264
  def _decompose_(self, qubits):
267
265
  yield cirq.X(qubits[0])
268
266
  yield cirq.Y(qubits[0]) ** 0.5
269
267
 
270
- class CompositeDummy2(cirq.testing.TwoQubitGate):
268
+ class CompositeExample2(cirq.testing.TwoQubitGate):
271
269
  def _decompose_(self, qubits):
272
270
  yield cirq.CZ(qubits[0], qubits[1])
273
- yield CompositeDummy()(qubits[1])
271
+ yield CompositeExample()(qubits[1])
274
272
 
275
273
  q0, q1 = cirq.LineQubit.range(2)
276
- circuit = cirq.Circuit(CompositeDummy()(q0), CompositeDummy2()(q0, q1))
274
+ circuit = cirq.Circuit(CompositeExample()(q0), CompositeExample2()(q0, q1))
277
275
  expected = cirq.Circuit(
278
276
  cirq.X(q0), cirq.Y(q0) ** 0.5, cirq.CZ(q0, q1), cirq.X(q1), cirq.Y(q1) ** 0.5
279
277
  )
@@ -290,11 +288,11 @@ def test_composite_gates_without_matrix():
290
288
 
291
289
 
292
290
  def test_unsupported_gate():
293
- class UnsupportedDummy(cirq.testing.TwoQubitGate):
291
+ class UnsupportedExample(cirq.testing.TwoQubitGate):
294
292
  pass
295
293
 
296
294
  q0, q1 = cirq.LineQubit.range(2)
297
- circuit = cirq.Circuit(UnsupportedDummy()(q0, q1))
295
+ circuit = cirq.Circuit(UnsupportedExample()(q0, q1))
298
296
  assert circuit == cirq.optimize_for_target_gateset(circuit, gateset=cirq.CZTargetGateset())
299
297
  with pytest.raises(ValueError, match='Unable to convert'):
300
298
  _ = cirq.optimize_for_target_gateset(