cirq-core 1.3.0.dev20231201141002__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cirq/__init__.py +4 -0
- cirq/_compat.py +9 -11
- cirq/_compat_test.py +45 -56
- cirq/_version.py +31 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/circuit.py +13 -8
- cirq/circuits/circuit_operation.py +2 -1
- cirq/circuits/circuit_test.py +2 -2
- cirq/circuits/frozen_circuit.py +3 -2
- cirq/circuits/moment.py +12 -10
- cirq/circuits/qasm_output.py +5 -1
- cirq/circuits/qasm_output_test.py +25 -10
- cirq/contrib/qcircuit/qcircuit_diagram_info.py +9 -7
- cirq/contrib/quimb/mps_simulator_test.py +1 -1
- cirq/contrib/quimb/state_vector.py +9 -2
- cirq/contrib/svg/svg.py +2 -1
- cirq/contrib/svg/svg_test.py +1 -0
- cirq/devices/grid_qubit.py +85 -32
- cirq/devices/grid_qubit_test.py +22 -4
- cirq/devices/line_qubit.py +74 -26
- cirq/devices/line_qubit_test.py +19 -0
- cirq/devices/noise_utils.py +33 -31
- cirq/devices/noise_utils_test.py +1 -84
- cirq/devices/superconducting_qubits_noise_properties.py +7 -6
- cirq/experiments/__init__.py +8 -0
- cirq/experiments/qubit_characterizations.py +288 -44
- cirq/experiments/qubit_characterizations_test.py +61 -7
- cirq/experiments/random_quantum_circuit_generation.py +1 -1
- cirq/experiments/single_qubit_readout_calibration.py +132 -6
- cirq/experiments/single_qubit_readout_calibration_test.py +3 -1
- cirq/experiments/t1_decay_experiment.py +14 -7
- cirq/experiments/t1_decay_experiment_test.py +14 -26
- cirq/experiments/two_qubit_xeb.py +483 -0
- cirq/experiments/two_qubit_xeb_test.py +304 -0
- cirq/json_resolver_cache.py +2 -0
- cirq/linalg/decompositions.py +11 -13
- cirq/linalg/decompositions_test.py +1 -3
- cirq/linalg/diagonalize.py +5 -4
- cirq/linalg/predicates.py +8 -6
- cirq/linalg/transformations.py +2 -1
- cirq/linalg/transformations_test.py +1 -1
- cirq/ops/__init__.py +2 -0
- cirq/ops/clifford_gate.py +59 -16
- cirq/ops/common_gates_test.py +1 -2
- cirq/ops/control_values.py +4 -3
- cirq/ops/controlled_gate_test.py +1 -3
- cirq/ops/gate_operation.py +10 -1
- cirq/ops/named_qubit.py +74 -28
- cirq/ops/named_qubit_test.py +19 -0
- cirq/ops/parity_gates.py +5 -0
- cirq/ops/parity_gates_test.py +2 -10
- cirq/ops/pauli_gates.py +5 -2
- cirq/ops/pauli_string.py +2 -2
- cirq/ops/permutation_gate.py +16 -18
- cirq/ops/phased_iswap_gate_test.py +1 -3
- cirq/ops/phased_x_gate.py +1 -1
- cirq/ops/phased_x_z_gate.py +17 -1
- cirq/ops/phased_x_z_gate_test.py +24 -0
- cirq/ops/qid_util.py +4 -8
- cirq/ops/qubit_manager.py +7 -4
- cirq/ops/qubit_manager_test.py +20 -0
- cirq/ops/raw_types.py +5 -2
- cirq/ops/raw_types_test.py +14 -15
- cirq/ops/uniform_superposition_gate.py +123 -0
- cirq/ops/uniform_superposition_gate_test.py +94 -0
- cirq/protocols/approximate_equality_protocol_test.py +2 -2
- cirq/protocols/circuit_diagram_info_protocol.py +6 -4
- cirq/protocols/commutes_protocol.py +2 -4
- cirq/protocols/decompose_protocol.py +7 -12
- cirq/protocols/decompose_protocol_test.py +7 -3
- cirq/protocols/has_stabilizer_effect_protocol.py +1 -5
- cirq/protocols/has_stabilizer_effect_protocol_test.py +13 -4
- cirq/protocols/json_serialization.py +51 -181
- cirq/protocols/json_serialization_test.py +13 -47
- cirq/protocols/json_test_data/CircuitOperation.json +131 -148
- cirq/protocols/json_test_data/CircuitOperation.json_inward +55 -0
- cirq/protocols/json_test_data/CircuitOperation.repr_inward +6 -0
- cirq/protocols/json_test_data/FrozenCircuit.json +196 -210
- cirq/protocols/json_test_data/FrozenCircuit.json_inward +35 -0
- cirq/protocols/json_test_data/FrozenCircuit.repr_inward +4 -0
- cirq/protocols/json_test_data/UniformSuperpositionGate.json +5 -0
- cirq/protocols/json_test_data/UniformSuperpositionGate.repr +1 -0
- cirq/protocols/json_test_data/cirq.MSGate.json +4 -0
- cirq/protocols/json_test_data/cirq.MSGate.repr +1 -0
- cirq/protocols/json_test_data/spec.py +2 -0
- cirq/protocols/pow_protocol_test.py +1 -3
- cirq/protocols/resolve_parameters.py +4 -2
- cirq/qis/__init__.py +10 -0
- cirq/qis/clifford_tableau.py +8 -2
- cirq/qis/noise_utils.py +123 -0
- cirq/qis/noise_utils_test.py +97 -0
- cirq/sim/classical_simulator.py +227 -87
- cirq/sim/classical_simulator_test.py +135 -0
- cirq/sim/clifford/clifford_simulator_test.py +4 -2
- cirq/sim/mux.py +5 -3
- cirq/sim/simulation_product_state.py +15 -10
- cirq/sim/simulation_state.py +1 -1
- cirq/sim/simulation_state_test.py +2 -2
- cirq/sim/simulator_base.py +3 -3
- cirq/sim/state_vector_simulation_state.py +4 -4
- cirq/sim/state_vector_simulator.py +17 -2
- cirq/study/__init__.py +1 -0
- cirq/study/result.py +14 -0
- cirq/study/result_test.py +6 -0
- cirq/study/sweeps.py +4 -2
- cirq/study/sweeps_test.py +8 -0
- cirq/testing/__init__.py +6 -1
- cirq/testing/_compat_test_data/__init__.py +3 -3
- cirq/testing/_compat_test_data/module_a/__init__.py +2 -2
- cirq/testing/circuit_compare.py +1 -1
- cirq/testing/consistent_qasm.py +6 -0
- cirq/testing/gate_features.py +10 -0
- cirq/testing/lin_alg_utils.py +5 -3
- cirq/transformers/__init__.py +15 -0
- cirq/transformers/analytical_decompositions/controlled_gate_decomposition.py +3 -1
- cirq/transformers/analytical_decompositions/two_qubit_to_cz.py +24 -0
- cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py +17 -0
- cirq/transformers/dynamical_decoupling.py +122 -0
- cirq/transformers/dynamical_decoupling_test.py +123 -0
- cirq/transformers/gauge_compiling/__init__.py +26 -0
- cirq/transformers/gauge_compiling/cz_gauge.py +46 -0
- cirq/transformers/gauge_compiling/cz_gauge_test.py +23 -0
- cirq/transformers/gauge_compiling/gauge_compiling.py +214 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test.py +41 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils.py +83 -0
- cirq/transformers/gauge_compiling/gauge_compiling_test_utils_test.py +52 -0
- cirq/transformers/gauge_compiling/iswap_gauge.py +105 -0
- cirq/transformers/gauge_compiling/iswap_gauge_test.py +23 -0
- cirq/transformers/gauge_compiling/spin_inversion_gauge.py +33 -0
- cirq/transformers/gauge_compiling/spin_inversion_gauge_test.py +37 -0
- cirq/transformers/gauge_compiling/sqrt_cz_gauge.py +64 -0
- cirq/transformers/gauge_compiling/sqrt_cz_gauge_test.py +27 -0
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge.py +94 -0
- cirq/transformers/gauge_compiling/sqrt_iswap_gauge_test.py +22 -0
- cirq/transformers/heuristic_decompositions/two_qubit_gate_tabulation.py +1 -0
- cirq/transformers/merge_k_qubit_gates_test.py +23 -23
- cirq/transformers/merge_single_qubit_gates_test.py +14 -14
- cirq/transformers/optimize_for_target_gateset.py +39 -17
- cirq/transformers/optimize_for_target_gateset_test.py +189 -39
- cirq/transformers/qubit_management_transformers.py +1 -1
- cirq/transformers/routing/visualize_routed_circuit_test.py +17 -17
- cirq/transformers/stratify_test.py +13 -13
- cirq/transformers/target_gatesets/compilation_target_gateset.py +26 -2
- cirq/transformers/target_gatesets/compilation_target_gateset_test.py +16 -16
- cirq/transformers/target_gatesets/cz_gateset.py +4 -0
- cirq/transformers/transformer_api.py +1 -2
- cirq/transformers/transformer_primitives.py +15 -14
- cirq/transformers/transformer_primitives_test.py +99 -72
- cirq/value/classical_data.py +6 -6
- cirq/value/value_equality_attr.py +4 -0
- cirq/work/sampler.py +3 -4
- cirq/work/sampler_test.py +25 -0
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/METADATA +10 -19
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/RECORD +157 -130
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/LICENSE +0 -0
- {cirq_core-1.3.0.dev20231201141002.dist-info → cirq_core-1.4.0.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""Transformers to rewrite a circuit using gates from a given target gateset."""
|
|
16
16
|
|
|
17
|
-
from typing import Optional, Callable, Hashable, Sequence, TYPE_CHECKING
|
|
17
|
+
from typing import Optional, Callable, Hashable, Sequence, TYPE_CHECKING, Union
|
|
18
18
|
|
|
19
19
|
from cirq import circuits
|
|
20
20
|
from cirq.protocols import decompose_protocol as dp
|
|
@@ -102,19 +102,29 @@ def optimize_for_target_gateset(
|
|
|
102
102
|
context: Optional['cirq.TransformerContext'] = None,
|
|
103
103
|
gateset: Optional['cirq.CompilationTargetGateset'] = None,
|
|
104
104
|
ignore_failures: bool = True,
|
|
105
|
+
max_num_passes: Union[int, None] = 1,
|
|
105
106
|
) -> 'cirq.Circuit':
|
|
106
107
|
"""Transforms the given circuit into an equivalent circuit using gates accepted by `gateset`.
|
|
107
108
|
|
|
109
|
+
Repeat max_num_passes times or when `max_num_passes=None` until no further changes can be done
|
|
108
110
|
1. Run all `gateset.preprocess_transformers`
|
|
109
111
|
2. Convert operations using built-in cirq decompose + `gateset.decompose_to_target_gateset`.
|
|
110
112
|
3. Run all `gateset.postprocess_transformers`
|
|
111
113
|
|
|
114
|
+
Note:
|
|
115
|
+
The optimizer is a heuristic and may not produce optimal results even with
|
|
116
|
+
max_num_passes=None. The preprocessors and postprocessors of the gate set
|
|
117
|
+
as well as their order yield different results.
|
|
118
|
+
|
|
119
|
+
|
|
112
120
|
Args:
|
|
113
121
|
circuit: Input circuit to transform. It will not be modified.
|
|
114
122
|
context: `cirq.TransformerContext` storing common configurable options for transformers.
|
|
115
123
|
gateset: Target gateset, which should be an instance of `cirq.CompilationTargetGateset`.
|
|
116
124
|
ignore_failures: If set, operations that fail to convert are left unchanged. If not set,
|
|
117
125
|
conversion failures raise a ValueError.
|
|
126
|
+
max_num_passes: The maximum number of passes to do. A value of `None` means to keep
|
|
127
|
+
iterating until no more changes happen to the number of moments or operations.
|
|
118
128
|
|
|
119
129
|
Returns:
|
|
120
130
|
An equivalent circuit containing gates accepted by `gateset`.
|
|
@@ -126,20 +136,32 @@ def optimize_for_target_gateset(
|
|
|
126
136
|
return _decompose_operations_to_target_gateset(
|
|
127
137
|
circuit, context=context, ignore_failures=ignore_failures
|
|
128
138
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
139
|
+
if isinstance(max_num_passes, int):
|
|
140
|
+
_outerloop = lambda: range(max_num_passes)
|
|
141
|
+
else:
|
|
142
|
+
|
|
143
|
+
def _outerloop():
|
|
144
|
+
while True:
|
|
145
|
+
yield 0
|
|
146
|
+
|
|
147
|
+
initial_num_moments, initial_num_ops = len(circuit), sum(1 for _ in circuit.all_operations())
|
|
148
|
+
for _ in _outerloop():
|
|
149
|
+
for transformer in gateset.preprocess_transformers:
|
|
150
|
+
circuit = transformer(circuit, context=context)
|
|
151
|
+
circuit = _decompose_operations_to_target_gateset(
|
|
152
|
+
circuit,
|
|
153
|
+
context=context,
|
|
154
|
+
gateset=gateset,
|
|
155
|
+
decomposer=gateset.decompose_to_target_gateset,
|
|
156
|
+
ignore_failures=ignore_failures,
|
|
157
|
+
tags_to_decompose=(gateset._intermediate_result_tag,),
|
|
158
|
+
)
|
|
159
|
+
for transformer in gateset.postprocess_transformers:
|
|
160
|
+
circuit = transformer(circuit, context=context)
|
|
161
|
+
|
|
162
|
+
num_moments, num_ops = len(circuit), sum(1 for _ in circuit.all_operations())
|
|
163
|
+
if (num_moments, num_ops) == (initial_num_moments, initial_num_ops):
|
|
164
|
+
# Stop early. No further optimizations can be done.
|
|
165
|
+
break
|
|
166
|
+
initial_num_moments, initial_num_ops = num_moments, num_ops
|
|
145
167
|
return circuit.unfreeze(copy=False)
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
from typing import Union
|
|
16
|
+
|
|
15
17
|
import cirq
|
|
16
18
|
from cirq.protocols.decompose_protocol import DecomposeResult
|
|
17
19
|
from cirq.transformers.optimize_for_target_gateset import _decompose_operations_to_target_gateset
|
|
@@ -51,22 +53,22 @@ def test_decompose_operations_to_target_gateset_default():
|
|
|
51
53
|
cirq.testing.assert_has_diagram(
|
|
52
54
|
c_orig,
|
|
53
55
|
'''
|
|
54
|
-
0: ───T───×───T───×[
|
|
55
|
-
│ │
|
|
56
|
-
1:
|
|
57
|
-
|
|
58
|
-
m:
|
|
56
|
+
0: ───T───×───T───×[ignore]───M───────T───×───T───
|
|
57
|
+
│ │ ║ │
|
|
58
|
+
1: ───────×───────×───────────╫───X───T───×───T───
|
|
59
|
+
║ ║
|
|
60
|
+
m: ═══════════════════════════@═══^═══════════════''',
|
|
59
61
|
)
|
|
60
62
|
context = cirq.TransformerContext(tags_to_ignore=("ignore",))
|
|
61
63
|
c_new = _decompose_operations_to_target_gateset(c_orig, context=context)
|
|
62
64
|
cirq.testing.assert_has_diagram(
|
|
63
65
|
c_new,
|
|
64
66
|
'''
|
|
65
|
-
0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[
|
|
66
|
-
│ │ │ │
|
|
67
|
-
1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5
|
|
68
|
-
|
|
69
|
-
m:
|
|
67
|
+
0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[ignore]───M───────T────────────@───Y^-0.5───@───Y^0.5────@───────────T───
|
|
68
|
+
│ │ │ │ ║ │ │ │
|
|
69
|
+
1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×───────────╫───X───T───Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───T───
|
|
70
|
+
║ ║
|
|
71
|
+
m: ══════════════════════════════════════════════════════════════════════@═══^══════════════════════════════════════════════════════════
|
|
70
72
|
''',
|
|
71
73
|
)
|
|
72
74
|
|
|
@@ -85,8 +87,8 @@ def test_decompose_operations_to_target_gateset():
|
|
|
85
87
|
cirq.T.on_each(*q),
|
|
86
88
|
)
|
|
87
89
|
gateset = cirq.Gateset(cirq.H, cirq.CNOT)
|
|
88
|
-
decomposer = (
|
|
89
|
-
|
|
90
|
+
decomposer = lambda op, _: (
|
|
91
|
+
cirq.H(op.qubits[0])
|
|
90
92
|
if cirq.has_unitary(op) and cirq.num_qubits(op) == 1
|
|
91
93
|
else NotImplemented
|
|
92
94
|
)
|
|
@@ -97,11 +99,11 @@ def test_decompose_operations_to_target_gateset():
|
|
|
97
99
|
cirq.testing.assert_has_diagram(
|
|
98
100
|
c_new,
|
|
99
101
|
'''
|
|
100
|
-
0: ───H───@───X───@───H───×[
|
|
101
|
-
│ │ │ │
|
|
102
|
-
1: ───────X───@───X
|
|
103
|
-
|
|
104
|
-
m:
|
|
102
|
+
0: ───H───@───X───@───H───×[ignore]───M───────H───@───X───@───H───
|
|
103
|
+
│ │ │ │ ║ │ │ │
|
|
104
|
+
1: ───────X───@───X───────×───────────╫───X───H───X───@───X───H───
|
|
105
|
+
║ ║
|
|
106
|
+
m: ═══════════════════════════════════@═══^═══════════════════════''',
|
|
105
107
|
)
|
|
106
108
|
|
|
107
109
|
with pytest.raises(ValueError, match="Unable to convert"):
|
|
@@ -134,9 +136,9 @@ def test_optimize_for_target_gateset_default():
|
|
|
134
136
|
cirq.testing.assert_has_diagram(
|
|
135
137
|
c_new,
|
|
136
138
|
'''
|
|
137
|
-
0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[
|
|
139
|
+
0: ───T────────────@───Y^-0.5───@───Y^0.5────@───────────T───×[ignore]───
|
|
138
140
|
│ │ │ │
|
|
139
|
-
1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5
|
|
141
|
+
1: ───────Y^-0.5───@───Y^0.5────@───Y^-0.5───@───Y^0.5───────×───────────
|
|
140
142
|
''',
|
|
141
143
|
)
|
|
142
144
|
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(c_orig, c_new, atol=1e-6)
|
|
@@ -157,15 +159,15 @@ def test_optimize_for_target_gateset():
|
|
|
157
159
|
cirq.testing.assert_has_diagram(
|
|
158
160
|
c_orig,
|
|
159
161
|
'''
|
|
160
|
-
0: ───qft───Y[
|
|
161
|
-
│
|
|
162
|
-
1: ───#2────Y[
|
|
163
|
-
│
|
|
164
|
-
2: ───#3────@[
|
|
165
|
-
│ │
|
|
166
|
-
3: ───#4────X
|
|
167
|
-
|
|
168
|
-
m:
|
|
162
|
+
0: ───qft───Y[ignore]───M───────qft^-1───
|
|
163
|
+
│ ║ │
|
|
164
|
+
1: ───#2────Y[ignore]───M───────#2───────
|
|
165
|
+
│ ║ │
|
|
166
|
+
2: ───#3────@[ignore]───╫───@───#3───────
|
|
167
|
+
│ │ ║ ║ │
|
|
168
|
+
3: ───#4────X───────────╫───@───#4───────
|
|
169
|
+
║ ║
|
|
170
|
+
m: ═════════════════════@═══^════════════
|
|
169
171
|
''',
|
|
170
172
|
)
|
|
171
173
|
gateset = MatrixGateTargetGateset()
|
|
@@ -174,17 +176,17 @@ m: ═══════════════════════@══
|
|
|
174
176
|
cirq.testing.assert_has_diagram(
|
|
175
177
|
c_new,
|
|
176
178
|
'''
|
|
177
|
-
┌────────┐
|
|
178
|
-
0: ───M[1]──────────M[1]──────────────────────M[1]────Y[
|
|
179
|
-
│ │ │
|
|
180
|
-
1: ───M[2]───M[1]───┼─────────────M[1]────M[1]┼───────Y[
|
|
181
|
-
│ │ │ │ │
|
|
182
|
-
2: ──────────M[2]───M[2]───M[1]───┼───────M[2]┼───────@[
|
|
183
|
-
│ │ │ │
|
|
184
|
-
3: ────────────────────────M[2]───M[2]────────M[2]────X
|
|
185
|
-
|
|
186
|
-
m:
|
|
187
|
-
└────────┘
|
|
179
|
+
┌────────┐ ┌────────┐ ┌────────┐
|
|
180
|
+
0: ───M[1]──────────M[1]──────────────────────M[1]────Y[ignore]───M────────────M[1]───────────────────M[1]────────M[1]───M[1]───
|
|
181
|
+
│ │ │ ║ │ │ │ │
|
|
182
|
+
1: ───M[2]───M[1]───┼─────────────M[1]────M[1]┼───────Y[ignore]───M────────M[1]┼──────────────M[1]────┼───M[1]────┼──────M[2]───
|
|
183
|
+
│ │ │ │ │ ║ │ │ │ │ │ │
|
|
184
|
+
2: ──────────M[2]───M[2]───M[1]───┼───────M[2]┼───────@[ignore]───╫───@────M[2]┼───────M[1]───┼───────┼───M[2]────M[2]──────────
|
|
185
|
+
│ │ │ │ ║ ║ │ │ │ │
|
|
186
|
+
3: ────────────────────────M[2]───M[2]────────M[2]────X───────────╫───@────────M[2]────M[2]───M[2]────M[2]──────────────────────
|
|
187
|
+
║ ║
|
|
188
|
+
m: ═══════════════════════════════════════════════════════════════@═══^═════════════════════════════════════════════════════════
|
|
189
|
+
└────────┘ └────────┘ └────────┘
|
|
188
190
|
''',
|
|
189
191
|
)
|
|
190
192
|
|
|
@@ -243,3 +245,151 @@ def test_optimize_for_target_gateset_deep():
|
|
|
243
245
|
1: ───#2───────────────────────────────────────────────────────────────────────────
|
|
244
246
|
''',
|
|
245
247
|
)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@pytest.mark.parametrize('max_num_passes', [2, None])
|
|
251
|
+
def test_optimize_for_target_gateset_multiple_passes(max_num_passes: Union[int, None]):
|
|
252
|
+
gateset = cirq.CZTargetGateset()
|
|
253
|
+
|
|
254
|
+
input_circuit = cirq.Circuit(
|
|
255
|
+
[
|
|
256
|
+
cirq.Moment(
|
|
257
|
+
cirq.X(cirq.LineQubit(1)),
|
|
258
|
+
cirq.X(cirq.LineQubit(2)),
|
|
259
|
+
cirq.X(cirq.LineQubit(3)),
|
|
260
|
+
cirq.X(cirq.LineQubit(6)),
|
|
261
|
+
),
|
|
262
|
+
cirq.Moment(
|
|
263
|
+
cirq.H(cirq.LineQubit(0)),
|
|
264
|
+
cirq.H(cirq.LineQubit(1)),
|
|
265
|
+
cirq.H(cirq.LineQubit(2)),
|
|
266
|
+
cirq.H(cirq.LineQubit(3)),
|
|
267
|
+
cirq.H(cirq.LineQubit(4)),
|
|
268
|
+
cirq.H(cirq.LineQubit(5)),
|
|
269
|
+
cirq.H(cirq.LineQubit(6)),
|
|
270
|
+
),
|
|
271
|
+
cirq.Moment(
|
|
272
|
+
cirq.H(cirq.LineQubit(1)), cirq.H(cirq.LineQubit(3)), cirq.H(cirq.LineQubit(5))
|
|
273
|
+
),
|
|
274
|
+
cirq.Moment(
|
|
275
|
+
cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
|
|
276
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
|
|
277
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
|
|
278
|
+
),
|
|
279
|
+
cirq.Moment(
|
|
280
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
|
|
281
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
|
|
282
|
+
cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
|
|
283
|
+
),
|
|
284
|
+
]
|
|
285
|
+
)
|
|
286
|
+
desired_circuit = cirq.Circuit.from_moments(
|
|
287
|
+
cirq.Moment(
|
|
288
|
+
cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
|
|
289
|
+
cirq.LineQubit(4)
|
|
290
|
+
)
|
|
291
|
+
),
|
|
292
|
+
cirq.Moment(cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5))),
|
|
293
|
+
cirq.Moment(
|
|
294
|
+
cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
|
|
295
|
+
cirq.LineQubit(1)
|
|
296
|
+
),
|
|
297
|
+
cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
|
|
298
|
+
cirq.LineQubit(0)
|
|
299
|
+
),
|
|
300
|
+
cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
|
|
301
|
+
cirq.LineQubit(3)
|
|
302
|
+
),
|
|
303
|
+
cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
|
|
304
|
+
cirq.LineQubit(2)
|
|
305
|
+
),
|
|
306
|
+
),
|
|
307
|
+
cirq.Moment(
|
|
308
|
+
cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
|
|
309
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
|
|
310
|
+
),
|
|
311
|
+
cirq.Moment(
|
|
312
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
|
|
313
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
|
|
314
|
+
),
|
|
315
|
+
cirq.Moment(
|
|
316
|
+
cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
|
|
317
|
+
cirq.LineQubit(6)
|
|
318
|
+
)
|
|
319
|
+
),
|
|
320
|
+
cirq.Moment(cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5))),
|
|
321
|
+
)
|
|
322
|
+
got = cirq.optimize_for_target_gateset(
|
|
323
|
+
input_circuit, gateset=gateset, max_num_passes=max_num_passes
|
|
324
|
+
)
|
|
325
|
+
cirq.testing.assert_same_circuits(got, desired_circuit)
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
@pytest.mark.parametrize('max_num_passes', [2, None])
|
|
329
|
+
def test_optimize_for_target_gateset_multiple_passes_dont_preserve_moment_structure(
|
|
330
|
+
max_num_passes: Union[int, None]
|
|
331
|
+
):
|
|
332
|
+
gateset = cirq.CZTargetGateset(preserve_moment_structure=False)
|
|
333
|
+
|
|
334
|
+
input_circuit = cirq.Circuit(
|
|
335
|
+
[
|
|
336
|
+
cirq.Moment(
|
|
337
|
+
cirq.X(cirq.LineQubit(1)),
|
|
338
|
+
cirq.X(cirq.LineQubit(2)),
|
|
339
|
+
cirq.X(cirq.LineQubit(3)),
|
|
340
|
+
cirq.X(cirq.LineQubit(6)),
|
|
341
|
+
),
|
|
342
|
+
cirq.Moment(
|
|
343
|
+
cirq.H(cirq.LineQubit(0)),
|
|
344
|
+
cirq.H(cirq.LineQubit(1)),
|
|
345
|
+
cirq.H(cirq.LineQubit(2)),
|
|
346
|
+
cirq.H(cirq.LineQubit(3)),
|
|
347
|
+
cirq.H(cirq.LineQubit(4)),
|
|
348
|
+
cirq.H(cirq.LineQubit(5)),
|
|
349
|
+
cirq.H(cirq.LineQubit(6)),
|
|
350
|
+
),
|
|
351
|
+
cirq.Moment(
|
|
352
|
+
cirq.H(cirq.LineQubit(1)), cirq.H(cirq.LineQubit(3)), cirq.H(cirq.LineQubit(5))
|
|
353
|
+
),
|
|
354
|
+
cirq.Moment(
|
|
355
|
+
cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
|
|
356
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
|
|
357
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
|
|
358
|
+
),
|
|
359
|
+
cirq.Moment(
|
|
360
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
|
|
361
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
|
|
362
|
+
cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
|
|
363
|
+
),
|
|
364
|
+
]
|
|
365
|
+
)
|
|
366
|
+
desired_circuit = cirq.Circuit(
|
|
367
|
+
cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
|
|
368
|
+
cirq.LineQubit(4)
|
|
369
|
+
),
|
|
370
|
+
cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
|
|
371
|
+
cirq.LineQubit(1)
|
|
372
|
+
),
|
|
373
|
+
cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
|
|
374
|
+
cirq.LineQubit(2)
|
|
375
|
+
),
|
|
376
|
+
cirq.PhasedXZGate(axis_phase_exponent=0.5, x_exponent=-0.5, z_exponent=1.0).on(
|
|
377
|
+
cirq.LineQubit(0)
|
|
378
|
+
),
|
|
379
|
+
cirq.PhasedXZGate(axis_phase_exponent=-1.0, x_exponent=1, z_exponent=0).on(
|
|
380
|
+
cirq.LineQubit(3)
|
|
381
|
+
),
|
|
382
|
+
cirq.PhasedXZGate(axis_phase_exponent=-0.5, x_exponent=0.5, z_exponent=0.0).on(
|
|
383
|
+
cirq.LineQubit(6)
|
|
384
|
+
),
|
|
385
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(5)),
|
|
386
|
+
cirq.CZ(cirq.LineQubit(0), cirq.LineQubit(1)),
|
|
387
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(3)),
|
|
388
|
+
cirq.CZ(cirq.LineQubit(2), cirq.LineQubit(1)),
|
|
389
|
+
cirq.CZ(cirq.LineQubit(4), cirq.LineQubit(3)),
|
|
390
|
+
cirq.CZ(cirq.LineQubit(6), cirq.LineQubit(5)),
|
|
391
|
+
)
|
|
392
|
+
got = cirq.optimize_for_target_gateset(
|
|
393
|
+
input_circuit, gateset=gateset, max_num_passes=max_num_passes
|
|
394
|
+
)
|
|
395
|
+
cirq.testing.assert_same_circuits(got, desired_circuit)
|
|
@@ -71,7 +71,7 @@ def map_clean_and_borrowable_qubits(
|
|
|
71
71
|
inserting all mapped operations in a resulting circuit using EARLIEST strategy. The reason
|
|
72
72
|
is that preserving moment structure forces additional constraints on the qubit allocation
|
|
73
73
|
strategy (i.e. if two operations `op1` and `op2` are in the same moment, then we cannot
|
|
74
|
-
reuse ancilla across `op1` and `op2`). We leave it
|
|
74
|
+
reuse ancilla across `op1` and `op2`). We leave it up to the user to force such constraints
|
|
75
75
|
using the qubit manager instead of making it part of the transformer.
|
|
76
76
|
3. However, for borrowable system qubits managed by the transformer, we do not reuse qubits
|
|
77
77
|
within the same moment.
|
|
@@ -20,15 +20,15 @@ def test_routed_circuit_with_mapping_simple():
|
|
|
20
20
|
q = cirq.LineQubit.range(2)
|
|
21
21
|
circuit = cirq.Circuit([cirq.Moment(cirq.SWAP(q[0], q[1]).with_tags(cirq.RoutingSwapTag()))])
|
|
22
22
|
expected_diagram = """
|
|
23
|
-
0: ───q(0)───×[
|
|
24
|
-
│ │
|
|
25
|
-
1: ───q(1)
|
|
23
|
+
0: ───q(0)───×[<r>]───q(1)───
|
|
24
|
+
│ │ │
|
|
25
|
+
1: ───q(1)───×────────q(0)───"""
|
|
26
26
|
cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
|
|
27
27
|
|
|
28
28
|
expected_diagram_with_initial_mapping = """
|
|
29
|
-
0: ───a───×[
|
|
30
|
-
│ │
|
|
31
|
-
1: ───b
|
|
29
|
+
0: ───a───×[<r>]───b───
|
|
30
|
+
│ │ │
|
|
31
|
+
1: ───b───×────────a───"""
|
|
32
32
|
cirq.testing.assert_has_diagram(
|
|
33
33
|
cirq.routed_circuit_with_mapping(
|
|
34
34
|
circuit, {cirq.NamedQubit("a"): q[0], cirq.NamedQubit("b"): q[1]}
|
|
@@ -74,16 +74,16 @@ def test_routed_circuit_with_mapping_multi_swaps():
|
|
|
74
74
|
]
|
|
75
75
|
)
|
|
76
76
|
expected_diagram = """
|
|
77
|
-
0: ───q(0)
|
|
78
|
-
│
|
|
79
|
-
1: ───q(1)───────────X
|
|
80
|
-
│ │
|
|
81
|
-
2: ───q(2)
|
|
82
|
-
│ │
|
|
83
|
-
3: ───q(3)───@───X
|
|
84
|
-
│ │ │
|
|
85
|
-
4: ───q(4)───X───X───×[
|
|
86
|
-
│ │
|
|
87
|
-
5: ───q(5)
|
|
77
|
+
0: ───q(0)────────────────────q(0)───×[<r>]───q(1)───────X───
|
|
78
|
+
│ │ │ │ │
|
|
79
|
+
1: ───q(1)───────────X────────q(1)───×────────q(0)───X───@───
|
|
80
|
+
│ │ │ │ │
|
|
81
|
+
2: ───q(2)───────@───@────────q(2)───×────────q(4)───@───────
|
|
82
|
+
│ │ │ │ │
|
|
83
|
+
3: ───q(3)───@───X───×────────q(4)───×[<r>]───q(2)───────────
|
|
84
|
+
│ │ │ │ │
|
|
85
|
+
4: ───q(4)───X───X───×[<r>]───q(3)────────────q(3)───────────
|
|
86
|
+
│ │ │ │
|
|
87
|
+
5: ───q(5)───────@────────────q(5)────────────q(5)───────────
|
|
88
88
|
"""
|
|
89
89
|
cirq.testing.assert_has_diagram(cirq.routed_circuit_with_mapping(circuit), expected_diagram)
|
|
@@ -237,29 +237,29 @@ def test_stratify_respects_no_compile_operations():
|
|
|
237
237
|
cirq.testing.assert_has_diagram(
|
|
238
238
|
input_circuit,
|
|
239
239
|
'''
|
|
240
|
-
0: ───X[
|
|
241
|
-
|
|
242
|
-
1: ───iSwap[
|
|
240
|
+
0: ───X[nocompile]───────X───────iSwap───
|
|
241
|
+
│
|
|
242
|
+
1: ───iSwap[nocompile]───────────iSwap───
|
|
243
243
|
│
|
|
244
|
-
2: ───iSwap
|
|
244
|
+
2: ───iSwap──────────────────────────────
|
|
245
245
|
|
|
246
|
-
3:
|
|
247
|
-
|
|
248
|
-
4: ───Z
|
|
246
|
+
3: ──────────────────────iSwap───X───────
|
|
247
|
+
│
|
|
248
|
+
4: ───Z──────────────────iSwap───────────
|
|
249
249
|
''',
|
|
250
250
|
)
|
|
251
251
|
cirq.testing.assert_has_diagram(
|
|
252
252
|
expected,
|
|
253
253
|
'''
|
|
254
|
-
0: ───────────────X[
|
|
255
|
-
|
|
256
|
-
1: ───────────────iSwap[
|
|
254
|
+
0: ───────────────X[nocompile]───────X───iSwap───
|
|
255
|
+
│
|
|
256
|
+
1: ───────────────iSwap[nocompile]───────iSwap───
|
|
257
257
|
│
|
|
258
|
-
2: ───────────────iSwap
|
|
258
|
+
2: ───────────────iSwap──────────────────────────
|
|
259
259
|
|
|
260
|
-
3: ───────iSwap
|
|
260
|
+
3: ───────iSwap──────────────────────X───────────
|
|
261
261
|
│
|
|
262
|
-
4: ───Z───iSwap
|
|
262
|
+
4: ───Z───iSwap──────────────────────────────────
|
|
263
263
|
''',
|
|
264
264
|
)
|
|
265
265
|
cirq.testing.assert_same_circuits(
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""Base class for creating custom target gatesets which can be used for compilation."""
|
|
16
16
|
|
|
17
|
-
from typing import Optional, List, Hashable, TYPE_CHECKING
|
|
17
|
+
from typing import Optional, List, Hashable, TYPE_CHECKING, Union, Type
|
|
18
18
|
import abc
|
|
19
19
|
|
|
20
20
|
from cirq import circuits, ops, protocols, transformers
|
|
@@ -80,6 +80,27 @@ class CompilationTargetGateset(ops.Gateset, metaclass=abc.ABCMeta):
|
|
|
80
80
|
which can transform any given circuit to contain gates accepted by this gateset.
|
|
81
81
|
"""
|
|
82
82
|
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
*gates: Union[Type['cirq.Gate'], 'cirq.Gate', 'cirq.GateFamily'],
|
|
86
|
+
name: Optional[str] = None,
|
|
87
|
+
unroll_circuit_op: bool = True,
|
|
88
|
+
preserve_moment_structure: bool = True,
|
|
89
|
+
):
|
|
90
|
+
"""Initializes CompilationTargetGateset.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
*gates: A list of `cirq.Gate` subclasses / `cirq.Gate` instances /
|
|
94
|
+
`cirq.GateFamily` instances to initialize the Gateset.
|
|
95
|
+
name: (Optional) Name for the Gateset. Useful for description.
|
|
96
|
+
unroll_circuit_op: If True, `cirq.CircuitOperation` is recursively
|
|
97
|
+
validated by validating the underlying `cirq.Circuit`.
|
|
98
|
+
preserve_moment_structure: Whether to preserve the moment structure of the
|
|
99
|
+
circuit during compilation or not.
|
|
100
|
+
"""
|
|
101
|
+
super().__init__(*gates, name=name, unroll_circuit_op=unroll_circuit_op)
|
|
102
|
+
self._preserve_moment_structure = preserve_moment_structure
|
|
103
|
+
|
|
83
104
|
@property
|
|
84
105
|
@abc.abstractmethod
|
|
85
106
|
def num_qubits(self) -> int:
|
|
@@ -140,11 +161,14 @@ class CompilationTargetGateset(ops.Gateset, metaclass=abc.ABCMeta):
|
|
|
140
161
|
@property
|
|
141
162
|
def postprocess_transformers(self) -> List['cirq.TRANSFORMER']:
|
|
142
163
|
"""List of transformers which should be run after decomposing individual operations."""
|
|
143
|
-
|
|
164
|
+
processors: List['cirq.TRANSFORMER'] = [
|
|
144
165
|
merge_single_qubit_gates.merge_single_qubit_moments_to_phxz,
|
|
145
166
|
transformers.drop_negligible_operations,
|
|
146
167
|
transformers.drop_empty_moments,
|
|
147
168
|
]
|
|
169
|
+
if not self._preserve_moment_structure:
|
|
170
|
+
processors.append(transformers.stratified_circuit)
|
|
171
|
+
return processors
|
|
148
172
|
|
|
149
173
|
|
|
150
174
|
class TwoQubitCompilationTargetGateset(CompilationTargetGateset):
|
|
@@ -138,9 +138,9 @@ def test_two_qubit_compilation_merge_and_replace_to_target_gateset():
|
|
|
138
138
|
cirq.testing.assert_has_diagram(
|
|
139
139
|
c_orig,
|
|
140
140
|
'''
|
|
141
|
-
0: ───X───@[
|
|
142
|
-
│
|
|
143
|
-
1: ───Z
|
|
141
|
+
0: ───X───@[no_compile]───Z───X───@───Z───X───
|
|
142
|
+
│ │
|
|
143
|
+
1: ───Z───@───────────────Z───────@───Z───────
|
|
144
144
|
''',
|
|
145
145
|
)
|
|
146
146
|
c_new = cirq.optimize_for_target_gateset(
|
|
@@ -151,9 +151,9 @@ def test_two_qubit_compilation_merge_and_replace_to_target_gateset():
|
|
|
151
151
|
cirq.testing.assert_has_diagram(
|
|
152
152
|
c_new,
|
|
153
153
|
'''
|
|
154
|
-
0: ───X───@[
|
|
155
|
-
│
|
|
156
|
-
1: ───Z
|
|
154
|
+
0: ───X───@[no_compile]───X───@───Y───@───Z───
|
|
155
|
+
│ │ │
|
|
156
|
+
1: ───Z───@───────────────X───X───Y───X───Z───
|
|
157
157
|
''',
|
|
158
158
|
)
|
|
159
159
|
|
|
@@ -178,11 +178,11 @@ def test_two_qubit_compilation_merge_and_replace_inefficient_component():
|
|
|
178
178
|
cirq.testing.assert_has_diagram(
|
|
179
179
|
c_orig,
|
|
180
180
|
'''
|
|
181
|
-
0: ───X───@───X───@[
|
|
182
|
-
│ │
|
|
183
|
-
1: ───────X
|
|
184
|
-
|
|
185
|
-
m:
|
|
181
|
+
0: ───X───@───X───@[no_compile]───Z───X───@───@───Z───X───@───M───────
|
|
182
|
+
│ │ │ │ │ ║
|
|
183
|
+
1: ───────X───────@───────────────Z───────X───X───Z───────X───╫───X───
|
|
184
|
+
║ ║
|
|
185
|
+
m: ═══════════════════════════════════════════════════════════@═══^═══
|
|
186
186
|
''',
|
|
187
187
|
)
|
|
188
188
|
c_new = cirq.optimize_for_target_gateset(
|
|
@@ -193,11 +193,11 @@ m: ═════════════════════════
|
|
|
193
193
|
cirq.testing.assert_has_diagram(
|
|
194
194
|
c_new,
|
|
195
195
|
'''
|
|
196
|
-
0: ───X───@───X───@[
|
|
197
|
-
│ │
|
|
198
|
-
1: ───────X
|
|
199
|
-
|
|
200
|
-
m:
|
|
196
|
+
0: ───X───@───X───@[no_compile]───X───@───Y───@───Z───M───────
|
|
197
|
+
│ │ │ │ ║
|
|
198
|
+
1: ───────X───────@───────────────X───X───Y───X───Z───╫───X───
|
|
199
|
+
║ ║
|
|
200
|
+
m: ═══════════════════════════════════════════════════@═══^═══
|
|
201
201
|
''',
|
|
202
202
|
)
|
|
203
203
|
|
|
@@ -48,6 +48,7 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
|
|
|
48
48
|
atol: float = 1e-8,
|
|
49
49
|
allow_partial_czs: bool = False,
|
|
50
50
|
additional_gates: Sequence[Union[Type['cirq.Gate'], 'cirq.Gate', 'cirq.GateFamily']] = (),
|
|
51
|
+
preserve_moment_structure: bool = True,
|
|
51
52
|
) -> None:
|
|
52
53
|
"""Initializes CZTargetGateset
|
|
53
54
|
|
|
@@ -57,6 +58,8 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
|
|
|
57
58
|
`cirq.CZ`, are part of this gateset.
|
|
58
59
|
additional_gates: Sequence of additional gates / gate families which should also
|
|
59
60
|
be "accepted" by this gateset. This is empty by default.
|
|
61
|
+
preserve_moment_structure: Whether to preserve the moment structure of the
|
|
62
|
+
circuit during compilation or not.
|
|
60
63
|
"""
|
|
61
64
|
super().__init__(
|
|
62
65
|
ops.CZPowGate if allow_partial_czs else ops.CZ,
|
|
@@ -65,6 +68,7 @@ class CZTargetGateset(compilation_target_gateset.TwoQubitCompilationTargetGatese
|
|
|
65
68
|
ops.GlobalPhaseGate,
|
|
66
69
|
*additional_gates,
|
|
67
70
|
name='CZPowTargetGateset' if allow_partial_czs else 'CZTargetGateset',
|
|
71
|
+
preserve_moment_structure=preserve_moment_structure,
|
|
68
72
|
)
|
|
69
73
|
self.additional_gates = tuple(
|
|
70
74
|
g if isinstance(g, ops.GateFamily) else ops.GateFamily(gate=g) for g in additional_gates
|
|
@@ -262,8 +262,7 @@ class TRANSFORMER(Protocol):
|
|
|
262
262
|
|
|
263
263
|
def __call__(
|
|
264
264
|
self, circuit: 'cirq.AbstractCircuit', *, context: Optional[TransformerContext] = None
|
|
265
|
-
) -> 'cirq.AbstractCircuit':
|
|
266
|
-
...
|
|
265
|
+
) -> 'cirq.AbstractCircuit': ...
|
|
267
266
|
|
|
268
267
|
|
|
269
268
|
_TRANSFORMER_T = TypeVar('_TRANSFORMER_T', bound=TRANSFORMER)
|