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.
- cirq/__init__.py +5 -0
- cirq/_compat.py +26 -11
- cirq/_compat_test.py +37 -3
- cirq/_version.py +31 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/circuit.py +106 -32
- cirq/circuits/circuit_operation.py +2 -2
- cirq/circuits/circuit_operation_test.py +1 -1
- cirq/circuits/circuit_test.py +109 -3
- cirq/circuits/frozen_circuit.py +80 -5
- cirq/circuits/frozen_circuit_test.py +47 -2
- cirq/circuits/qasm_output_test.py +9 -9
- cirq/conftest.py +1 -2
- cirq/contrib/acquaintance/devices.py +1 -1
- cirq/contrib/hacks/disable_validation_test.py +1 -1
- cirq/contrib/noise_models/noise_models.py +1 -2
- cirq/contrib/paulistring/clifford_optimize.py +1 -1
- cirq/contrib/paulistring/clifford_target_gateset_test.py +4 -4
- cirq/contrib/qcircuit/qcircuit_pdf.py +1 -1
- cirq/contrib/quimb/density_matrix.py +2 -3
- cirq/contrib/quimb/grid_circuits.py +3 -3
- cirq/contrib/quimb/state_vector.py +3 -5
- cirq/contrib/routing/utils.py +1 -2
- cirq/contrib/svg/svg.py +4 -6
- cirq/devices/grid_qubit.py +49 -38
- cirq/devices/grid_qubit_test.py +1 -3
- cirq/devices/insertion_noise_model.py +21 -1
- cirq/devices/insertion_noise_model_test.py +6 -0
- cirq/devices/line_qubit.py +67 -40
- cirq/devices/named_topologies.py +8 -14
- cirq/devices/noise_properties.py +1 -1
- cirq/devices/noise_utils.py +7 -5
- cirq/devices/noise_utils_test.py +7 -0
- cirq/experiments/fidelity_estimation_test.py +1 -1
- cirq/experiments/qubit_characterizations.py +6 -5
- cirq/experiments/random_quantum_circuit_generation.py +1 -1
- cirq/experiments/random_quantum_circuit_generation_test.py +28 -1
- cirq/experiments/readout_confusion_matrix.py +6 -6
- cirq/experiments/xeb_fitting.py +3 -5
- cirq/experiments/xeb_fitting_test.py +2 -2
- cirq/experiments/xeb_sampling.py +1 -1
- cirq/interop/quirk/url_to_circuit.py +40 -38
- cirq/json_resolver_cache.py +2 -0
- cirq/linalg/decompositions.py +6 -5
- cirq/ops/__init__.py +2 -0
- cirq/ops/classically_controlled_operation.py +1 -1
- cirq/ops/clifford_gate.py +9 -9
- cirq/ops/clifford_gate_test.py +3 -4
- cirq/ops/common_channels.py +2 -5
- cirq/ops/common_channels_test.py +3 -5
- cirq/ops/common_gates_test.py +7 -7
- cirq/ops/controlled_operation_test.py +2 -2
- cirq/ops/dense_pauli_string.py +3 -0
- cirq/ops/eigen_gate_test.py +1 -3
- cirq/ops/fourier_transform.py +1 -2
- cirq/ops/fsim_gate.py +1 -1
- cirq/ops/gate_features_test.py +2 -2
- cirq/ops/gate_operation_test.py +1 -2
- cirq/ops/greedy_qubit_manager.py +86 -0
- cirq/ops/greedy_qubit_manager_test.py +98 -0
- cirq/ops/linear_combinations.py +1 -1
- cirq/ops/named_qubit.py +55 -18
- cirq/ops/parity_gates.py +65 -18
- cirq/ops/parity_gates_test.py +41 -2
- cirq/ops/pauli_gates.py +2 -2
- cirq/ops/pauli_string.py +3 -4
- cirq/ops/pauli_string_raw_types_test.py +3 -3
- cirq/ops/pauli_string_test.py +3 -4
- cirq/ops/random_gate_channel_test.py +3 -3
- cirq/ops/raw_types.py +1 -1
- cirq/ops/raw_types_test.py +5 -5
- cirq/ops/three_qubit_gates.py +12 -8
- cirq/protocols/act_on_protocol_test.py +9 -9
- cirq/protocols/apply_channel_protocol.py +9 -6
- cirq/protocols/apply_unitary_protocol_test.py +1 -1
- cirq/protocols/equal_up_to_global_phase_protocol_test.py +2 -2
- cirq/protocols/has_stabilizer_effect_protocol.py +52 -6
- cirq/protocols/has_stabilizer_effect_protocol_test.py +21 -8
- cirq/protocols/has_unitary_protocol_test.py +1 -3
- cirq/protocols/json_serialization.py +6 -6
- cirq/protocols/json_serialization_test.py +7 -14
- cirq/protocols/json_test_data/InsertionNoiseModel.json +91 -0
- cirq/protocols/json_test_data/InsertionNoiseModel.repr +4 -0
- cirq/protocols/json_test_data/OpIdentifier.json +45 -10
- cirq/protocols/json_test_data/OpIdentifier.repr +7 -1
- cirq/protocols/json_test_data/spec.py +4 -0
- cirq/protocols/measurement_key_protocol_test.py +1 -1
- cirq/protocols/unitary_protocol_test.py +13 -16
- cirq/qis/clifford_tableau.py +7 -8
- cirq/qis/measures.py +1 -1
- cirq/qis/states.py +2 -3
- cirq/sim/__init__.py +2 -0
- cirq/sim/classical_simulator.py +107 -0
- cirq/sim/classical_simulator_test.py +207 -0
- cirq/sim/clifford/clifford_simulator_test.py +7 -7
- cirq/sim/clifford/stabilizer_simulation_state.py +2 -2
- cirq/sim/clifford/stabilizer_state_ch_form.py +7 -7
- cirq/sim/density_matrix_simulation_state.py +19 -4
- cirq/sim/density_matrix_simulator_test.py +5 -13
- cirq/sim/simulation_state_test.py +13 -14
- cirq/sim/simulator_test.py +6 -9
- cirq/sim/state_vector_simulation_state.py +1 -1
- cirq/study/resolver.py +41 -41
- cirq/study/resolver_test.py +13 -12
- cirq/testing/__init__.py +4 -1
- cirq/testing/circuit_compare.py +1 -1
- cirq/testing/circuit_compare_test.py +11 -11
- cirq/testing/consistent_controlled_gate_op.py +15 -1
- cirq/testing/consistent_controlled_gate_op_test.py +12 -3
- cirq/testing/consistent_decomposition.py +0 -1
- cirq/testing/consistent_protocols.py +6 -1
- cirq/testing/consistent_protocols_test.py +5 -10
- cirq/testing/consistent_qasm.py +2 -4
- cirq/testing/consistent_qasm_test.py +2 -3
- cirq/testing/consistent_specified_has_unitary_test.py +1 -3
- cirq/testing/equals_tester.py +1 -1
- cirq/testing/equals_tester_test.py +5 -5
- cirq/testing/equivalent_repr_eval_test.py +1 -3
- cirq/testing/gate_features_test.py +6 -6
- cirq/testing/order_tester_test.py +1 -3
- cirq/testing/random_circuit_test.py +1 -3
- cirq/transformers/__init__.py +3 -0
- cirq/transformers/analytical_decompositions/__init__.py +1 -0
- cirq/transformers/analytical_decompositions/three_qubit_decomposition.py +1 -2
- cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +2 -5
- cirq/transformers/analytical_decompositions/two_qubit_state_preparation.py +38 -0
- cirq/transformers/analytical_decompositions/two_qubit_state_preparation_test.py +18 -0
- cirq/transformers/expand_composite_test.py +4 -4
- cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +1 -1
- cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -2
- cirq/transformers/merge_k_qubit_gates_test.py +2 -2
- cirq/transformers/qubit_management_transformers.py +177 -0
- cirq/transformers/qubit_management_transformers_test.py +250 -0
- cirq/transformers/routing/route_circuit_cqc.py +23 -4
- cirq/transformers/routing/route_circuit_cqc_test.py +42 -0
- cirq/transformers/stratify.py +10 -11
- cirq/transformers/target_gatesets/compilation_target_gateset_test.py +10 -10
- cirq/transformers/target_gatesets/cz_gateset_test.py +8 -10
- cirq/transformers/transformer_primitives.py +138 -28
- cirq/value/abc_alt_test.py +4 -4
- cirq/value/duration.py +68 -37
- cirq/value/duration_test.py +2 -0
- cirq/value/measurement_key_test.py +1 -1
- cirq/value/product_state.py +4 -8
- cirq/value/value_equality_attr.py +12 -5
- cirq/vis/heatmap.py +7 -4
- cirq/vis/heatmap_test.py +14 -4
- cirq/vis/histogram.py +4 -4
- cirq/vis/state_histogram.py +10 -6
- cirq/vis/state_histogram_test.py +2 -0
- cirq/work/observable_measurement_data_test.py +1 -1
- cirq/work/observable_measurement_test.py +2 -2
- cirq/work/zeros_sampler.py +1 -1
- {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/METADATA +11 -19
- {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/RECORD +158 -150
- {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.2.0.dev20230717225858.dist-info → cirq_core-1.3.0.dist-info}/LICENSE +0 -0
- {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
|
-
|
|
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 [
|
|
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
|
-
|
|
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)
|
|
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
|
cirq/transformers/stratify.py
CHANGED
|
@@ -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
|
|
187
|
-
# classifier for otherwise unclassified ops.
|
|
188
|
-
classifiers = [_category_to_classifier(cat) for cat in categories] + [
|
|
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
|
|
226
|
-
"""
|
|
227
|
-
return False #
|
|
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
|
|
234
|
-
|
|
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
|
|
237
|
+
# so return the index of the mock classifer.
|
|
239
238
|
try:
|
|
240
|
-
return
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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=
|
|
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 =
|
|
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=
|
|
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=
|
|
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
|
|
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=
|
|
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
|
|
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
|
|
268
|
+
class CompositeExample2(cirq.testing.TwoQubitGate):
|
|
271
269
|
def _decompose_(self, qubits):
|
|
272
270
|
yield cirq.CZ(qubits[0], qubits[1])
|
|
273
|
-
yield
|
|
271
|
+
yield CompositeExample()(qubits[1])
|
|
274
272
|
|
|
275
273
|
q0, q1 = cirq.LineQubit.range(2)
|
|
276
|
-
circuit = cirq.Circuit(
|
|
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
|
|
291
|
+
class UnsupportedExample(cirq.testing.TwoQubitGate):
|
|
294
292
|
pass
|
|
295
293
|
|
|
296
294
|
q0, q1 = cirq.LineQubit.range(2)
|
|
297
|
-
circuit = cirq.Circuit(
|
|
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(
|