cirq-core 1.2.0.dev20230717232332__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/METADATA +11 -19
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/RECORD +158 -150
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/LICENSE +0 -0
- {cirq_core-1.2.0.dev20230717232332.dist-info → cirq_core-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -54,8 +54,7 @@ class QuditGate(cirq.Gate):
|
|
|
54
54
|
def test_assert_qasm_is_consistent_with_unitary():
|
|
55
55
|
try:
|
|
56
56
|
import qiskit as _
|
|
57
|
-
except ImportError:
|
|
58
|
-
# coverage: ignore
|
|
57
|
+
except ImportError: # pragma: no cover
|
|
59
58
|
warnings.warn(
|
|
60
59
|
"Skipped test_assert_qasm_is_consistent_with_unitary "
|
|
61
60
|
"because qiskit isn't installed to verify against."
|
|
@@ -84,7 +83,7 @@ def test_assert_qasm_is_consistent_with_unitary():
|
|
|
84
83
|
)
|
|
85
84
|
|
|
86
85
|
# Checks that code is valid.
|
|
87
|
-
with pytest.raises(AssertionError, match='
|
|
86
|
+
with pytest.raises(AssertionError, match='QASM not consistent'):
|
|
88
87
|
cirq.testing.assert_qasm_is_consistent_with_unitary(
|
|
89
88
|
Fixed(np.array([[1, 0], [0, -1]]), 'JUNK$&*@($#::=[];')
|
|
90
89
|
)
|
|
@@ -32,12 +32,10 @@ def test_assert_specifies_has_unitary_if_unitary_from_apply():
|
|
|
32
32
|
class Bad(cirq.Operation):
|
|
33
33
|
@property
|
|
34
34
|
def qubits(self):
|
|
35
|
-
# coverage: ignore
|
|
36
35
|
return ()
|
|
37
36
|
|
|
38
37
|
def with_qubits(self, *new_qubits):
|
|
39
|
-
#
|
|
40
|
-
return self
|
|
38
|
+
return self # pragma: no cover
|
|
41
39
|
|
|
42
40
|
def _apply_unitary_(self, args):
|
|
43
41
|
return args.target_tensor
|
cirq/testing/equals_tester.py
CHANGED
|
@@ -169,16 +169,16 @@ def test_fails_when_ne_is_inconsistent():
|
|
|
169
169
|
|
|
170
170
|
def __eq__(self, other):
|
|
171
171
|
if not isinstance(other, type(self)):
|
|
172
|
-
return NotImplemented #
|
|
172
|
+
return NotImplemented # pragma: no cover
|
|
173
173
|
return self.x == other.x
|
|
174
174
|
|
|
175
175
|
def __ne__(self, other):
|
|
176
176
|
if not isinstance(other, type(self)):
|
|
177
|
-
return NotImplemented #
|
|
177
|
+
return NotImplemented # pragma: no cover
|
|
178
178
|
return self.x == other.x
|
|
179
179
|
|
|
180
180
|
def __hash__(self):
|
|
181
|
-
return hash(self.x) #
|
|
181
|
+
return hash(self.x) # pragma: no cover
|
|
182
182
|
|
|
183
183
|
with pytest.raises(AssertionError, match='inconsistent'):
|
|
184
184
|
eq.make_equality_group(InconsistentNeImplementation)
|
|
@@ -278,7 +278,7 @@ def test_returns_not_implemented_for_other_types():
|
|
|
278
278
|
if isinstance(other, (FirstClass, SecondClass)):
|
|
279
279
|
return self.val == other.val
|
|
280
280
|
# Ignore coverage, this is just for illustrative purposes.
|
|
281
|
-
return NotImplemented #
|
|
281
|
+
return NotImplemented # pragma: no cover
|
|
282
282
|
|
|
283
283
|
# But we see that this does not work because it fails commutativity of ==
|
|
284
284
|
assert SecondClass("a") == FirstClass("a")
|
|
@@ -306,7 +306,7 @@ def test_returns_not_implemented_for_other_types():
|
|
|
306
306
|
if isinstance(other, (ThirdClass, FourthClass)):
|
|
307
307
|
return self.val == other.val
|
|
308
308
|
# Ignore coverage, this is just for illustrative purposes.
|
|
309
|
-
return NotImplemented #
|
|
309
|
+
return NotImplemented # pragma: no cover
|
|
310
310
|
|
|
311
311
|
# We see this is fixed:
|
|
312
312
|
assert ThirdClass("a") == FourthClass("a")
|
|
@@ -26,11 +26,11 @@ def test_two_qubit_gate_is_abstract_can_implement():
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def test_two_qubit_gate_validate_pass():
|
|
29
|
-
class
|
|
29
|
+
class Example(cirq.testing.TwoQubitGate):
|
|
30
30
|
def matrix(self):
|
|
31
31
|
pass
|
|
32
32
|
|
|
33
|
-
g =
|
|
33
|
+
g = Example()
|
|
34
34
|
q1 = cirq.NamedQubit('q1')
|
|
35
35
|
q2 = cirq.NamedQubit('q2')
|
|
36
36
|
q3 = cirq.NamedQubit('q3')
|
|
@@ -42,11 +42,11 @@ def test_two_qubit_gate_validate_pass():
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def test_two_qubit_gate_validate_wrong_number():
|
|
45
|
-
class
|
|
45
|
+
class Example(cirq.testing.TwoQubitGate):
|
|
46
46
|
def matrix(self):
|
|
47
47
|
pass
|
|
48
48
|
|
|
49
|
-
g =
|
|
49
|
+
g = Example()
|
|
50
50
|
q1 = cirq.NamedQubit('q1')
|
|
51
51
|
q2 = cirq.NamedQubit('q2')
|
|
52
52
|
q3 = cirq.NamedQubit('q3')
|
|
@@ -60,11 +60,11 @@ def test_two_qubit_gate_validate_wrong_number():
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
def test_three_qubit_gate_validate():
|
|
63
|
-
class
|
|
63
|
+
class Example(cirq.testing.ThreeQubitGate):
|
|
64
64
|
def matrix(self):
|
|
65
65
|
pass
|
|
66
66
|
|
|
67
|
-
g =
|
|
67
|
+
g = Example()
|
|
68
68
|
a, b, c, d = cirq.LineQubit.range(4)
|
|
69
69
|
|
|
70
70
|
assert g.num_qubits() == 3
|
|
@@ -60,8 +60,7 @@ def _cases_for_random_circuit():
|
|
|
60
60
|
# number of qubits greater that the number of qubits for the
|
|
61
61
|
# circuit. In this case, try again.
|
|
62
62
|
if all(n > n_qubits for n in gate_domain.values()):
|
|
63
|
-
#
|
|
64
|
-
continue
|
|
63
|
+
continue # pragma: no cover
|
|
65
64
|
else:
|
|
66
65
|
gate_domain = None
|
|
67
66
|
pass_qubits = random.choice((True, False))
|
|
@@ -107,7 +106,6 @@ def test_random_circuit_reproducible_with_seed(seed):
|
|
|
107
106
|
|
|
108
107
|
|
|
109
108
|
def test_random_circuit_not_expected_number_of_qubits():
|
|
110
|
-
|
|
111
109
|
circuit = cirq.testing.random_circuit(
|
|
112
110
|
qubits=3, n_moments=1, op_density=1.0, gate_domain={cirq.CNOT: 2}
|
|
113
111
|
)
|
cirq/transformers/__init__.py
CHANGED
|
@@ -24,6 +24,7 @@ from cirq.transformers.analytical_decompositions import (
|
|
|
24
24
|
is_negligible_turn,
|
|
25
25
|
parameterized_2q_op_to_sqrt_iswap_operations,
|
|
26
26
|
prepare_two_qubit_state_using_cz,
|
|
27
|
+
prepare_two_qubit_state_using_iswap,
|
|
27
28
|
prepare_two_qubit_state_using_sqrt_iswap,
|
|
28
29
|
quantum_shannon_decomposition,
|
|
29
30
|
single_qubit_matrix_to_gates,
|
|
@@ -93,6 +94,8 @@ from cirq.transformers.merge_single_qubit_gates import (
|
|
|
93
94
|
merge_single_qubit_moments_to_phxz,
|
|
94
95
|
)
|
|
95
96
|
|
|
97
|
+
from cirq.transformers.qubit_management_transformers import map_clean_and_borrowable_qubits
|
|
98
|
+
|
|
96
99
|
from cirq.transformers.synchronize_terminal_measurements import synchronize_terminal_measurements
|
|
97
100
|
|
|
98
101
|
from cirq.transformers.transformer_api import (
|
|
@@ -66,6 +66,7 @@ from cirq.transformers.analytical_decompositions.two_qubit_to_sqrt_iswap import
|
|
|
66
66
|
from cirq.transformers.analytical_decompositions.two_qubit_state_preparation import (
|
|
67
67
|
prepare_two_qubit_state_using_cz,
|
|
68
68
|
prepare_two_qubit_state_using_sqrt_iswap,
|
|
69
|
+
prepare_two_qubit_state_using_iswap,
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
from cirq.transformers.analytical_decompositions.single_to_two_qubit_isometry import (
|
|
@@ -56,8 +56,7 @@ def three_qubit_matrix_to_operations(
|
|
|
56
56
|
|
|
57
57
|
try:
|
|
58
58
|
from scipy.linalg import cossin
|
|
59
|
-
except ImportError: #
|
|
60
|
-
# coverage: ignore
|
|
59
|
+
except ImportError: # pragma: no cover
|
|
61
60
|
raise ImportError(
|
|
62
61
|
"cirq.three_qubit_unitary_to_operations requires "
|
|
63
62
|
"SciPy 1.5.0+, as it uses the cossin function. Please"
|
|
@@ -30,15 +30,13 @@ from cirq.transformers.analytical_decompositions.three_qubit_decomposition impor
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def _skip_if_scipy(*, version_is_greater_than_1_5_0: bool) -> Callable[[Callable], Callable]:
|
|
33
|
-
def decorator(func):
|
|
33
|
+
def decorator(func): # pragma: no cover
|
|
34
34
|
try:
|
|
35
35
|
# pylint: disable=unused-import
|
|
36
36
|
from scipy.linalg import cossin
|
|
37
37
|
|
|
38
|
-
# coverage: ignore
|
|
39
38
|
return None if version_is_greater_than_1_5_0 else func
|
|
40
39
|
except ImportError:
|
|
41
|
-
# coverage: ignore
|
|
42
40
|
return func if version_is_greater_than_1_5_0 else None
|
|
43
41
|
|
|
44
42
|
return decorator
|
|
@@ -84,8 +82,7 @@ def test_three_qubit_matrix_to_operations_errors():
|
|
|
84
82
|
# environment like that, we'll need to ignore the coverage somehow conditionally on
|
|
85
83
|
# the scipy version.
|
|
86
84
|
@_skip_if_scipy(version_is_greater_than_1_5_0=True)
|
|
87
|
-
#
|
|
88
|
-
def test_three_qubit_matrix_to_operations_scipy_error():
|
|
85
|
+
def test_three_qubit_matrix_to_operations_scipy_error(): # pragma: no cover
|
|
89
86
|
a, b, c = cirq.LineQubit.range(3)
|
|
90
87
|
with pytest.raises(ImportError, match="three_qubit.*1.5.0+"):
|
|
91
88
|
cirq.three_qubit_matrix_to_operations(a, b, c, np.eye(8))
|
|
@@ -106,3 +106,41 @@ def prepare_two_qubit_state_using_cz(
|
|
|
106
106
|
return op_list + _1q_matrices_to_ops(
|
|
107
107
|
np.dot(u, np.linalg.inv(u_CZ)), np.dot(vh.T, np.linalg.inv(vh_CZ.T)), q0, q1
|
|
108
108
|
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def prepare_two_qubit_state_using_iswap(
|
|
112
|
+
q0: 'cirq.Qid', q1: 'cirq.Qid', state: 'cirq.STATE_VECTOR_LIKE', use_iswap_inv: bool = False
|
|
113
|
+
) -> List['cirq.Operation']:
|
|
114
|
+
"""Prepares the given 2q state from |00> using at-most 1 ISWAP gate + single qubit rotations.
|
|
115
|
+
|
|
116
|
+
Entangled states are prepared using exactly 1 ISWAP gate while product states are prepared
|
|
117
|
+
using only single qubit rotations (0 ISWAP gates)
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
q0: The first qubit being operated on.
|
|
121
|
+
q1: The other qubit being operated on.
|
|
122
|
+
state: 4x1 matrix representing two qubit state vector, ordered as 00, 01, 10, 11.
|
|
123
|
+
use_iswap_inv: If True, uses `cirq.ISWAP_INV` instead of `cirq.ISWAP`.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
List of operations (at-most 1 ISWAP + single qubit rotations) preparing state from |00>.
|
|
127
|
+
"""
|
|
128
|
+
state_vector = qis.to_valid_state_vector(state, num_qubits=2)
|
|
129
|
+
state_vector = state_vector / np.linalg.norm(state_vector)
|
|
130
|
+
u, s, vh = np.linalg.svd(state_vector.reshape(2, 2))
|
|
131
|
+
if np.isclose(s[0], 1):
|
|
132
|
+
# Product state can be prepare with just single qubit unitaries.
|
|
133
|
+
return _1q_matrices_to_ops(u, vh.T, q0, q1, True)
|
|
134
|
+
alpha = np.arccos(np.clip(s[0], 0, 1))
|
|
135
|
+
op_list = [
|
|
136
|
+
ops.ry(2 * alpha).on(q0),
|
|
137
|
+
ops.H.on(q1),
|
|
138
|
+
ops.ISWAP_INV.on(q0, q1) if use_iswap_inv else ops.ISWAP.on(q0, q1),
|
|
139
|
+
]
|
|
140
|
+
intermediate_state = circuits.Circuit(op_list).final_state_vector(
|
|
141
|
+
ignore_terminal_measurements=False, dtype=np.complex64
|
|
142
|
+
)
|
|
143
|
+
u_CZ, _, vh_CZ = np.linalg.svd(intermediate_state.reshape(2, 2))
|
|
144
|
+
return op_list + _1q_matrices_to_ops(
|
|
145
|
+
np.dot(u, np.linalg.inv(u_CZ)), np.dot(vh.T, np.linalg.inv(vh_CZ.T)), q0, q1
|
|
146
|
+
)
|
|
@@ -71,6 +71,24 @@ def test_prepare_two_qubit_state_using_cz(state):
|
|
|
71
71
|
)
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
@pytest.mark.parametrize("state", STATES_TO_PREPARE)
|
|
75
|
+
@pytest.mark.parametrize("use_iswap_inv", [True, False])
|
|
76
|
+
def test_prepare_two_qubit_state_using_iswap(state, use_iswap_inv):
|
|
77
|
+
state = cirq.to_valid_state_vector(state, num_qubits=2)
|
|
78
|
+
q = cirq.LineQubit.range(2)
|
|
79
|
+
circuit = cirq.Circuit(
|
|
80
|
+
cirq.prepare_two_qubit_state_using_iswap(*q, state, use_iswap_inv=use_iswap_inv)
|
|
81
|
+
)
|
|
82
|
+
iswap_gate = cirq.ISWAP_INV if use_iswap_inv else cirq.ISWAP
|
|
83
|
+
ops_iswap = [*circuit.findall_operations(lambda op: op.gate == iswap_gate)]
|
|
84
|
+
ops_2q = [*circuit.findall_operations(lambda op: cirq.num_qubits(op) > 1)]
|
|
85
|
+
assert ops_iswap == ops_2q
|
|
86
|
+
assert len(ops_iswap) <= 1
|
|
87
|
+
assert cirq.allclose_up_to_global_phase(
|
|
88
|
+
circuit.final_state_vector(ignore_terminal_measurements=False, dtype=np.complex64), state
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
74
92
|
@pytest.mark.parametrize("state", STATES_TO_PREPARE)
|
|
75
93
|
@pytest.mark.parametrize("use_sqrt_iswap_inv", [True, False])
|
|
76
94
|
def test_prepare_two_qubit_state_using_sqrt_iswap(state, use_sqrt_iswap_inv):
|
|
@@ -105,14 +105,14 @@ def test_recursive_composite():
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
def test_decompose_returns_not_flat_op_tree():
|
|
108
|
-
class
|
|
108
|
+
class ExampleGate(cirq.testing.SingleQubitGate):
|
|
109
109
|
def _decompose_(self, qubits):
|
|
110
110
|
(q0,) = qubits
|
|
111
111
|
# Yield a tuple of gates instead of yielding a gate
|
|
112
112
|
yield cirq.X(q0),
|
|
113
113
|
|
|
114
114
|
q0 = cirq.NamedQubit('q0')
|
|
115
|
-
circuit = cirq.Circuit(
|
|
115
|
+
circuit = cirq.Circuit(ExampleGate()(q0))
|
|
116
116
|
|
|
117
117
|
circuit = cirq.expand_composite(circuit)
|
|
118
118
|
expected = cirq.Circuit(cirq.X(q0))
|
|
@@ -120,7 +120,7 @@ def test_decompose_returns_not_flat_op_tree():
|
|
|
120
120
|
|
|
121
121
|
|
|
122
122
|
def test_decompose_returns_deep_op_tree():
|
|
123
|
-
class
|
|
123
|
+
class ExampleGate(cirq.testing.TwoQubitGate):
|
|
124
124
|
def _decompose_(self, qubits):
|
|
125
125
|
q0, q1 = qubits
|
|
126
126
|
# Yield a tuple
|
|
@@ -139,7 +139,7 @@ def test_decompose_returns_deep_op_tree():
|
|
|
139
139
|
yield generator(2)
|
|
140
140
|
|
|
141
141
|
q0, q1 = cirq.LineQubit.range(2)
|
|
142
|
-
circuit = cirq.Circuit(
|
|
142
|
+
circuit = cirq.Circuit(ExampleGate()(q0, q1))
|
|
143
143
|
|
|
144
144
|
circuit = cirq.expand_composite(circuit)
|
|
145
145
|
expected = cirq.Circuit(
|
|
@@ -174,7 +174,7 @@ def kak_vector_infidelity(
|
|
|
174
174
|
|
|
175
175
|
# Ensure we consider equivalent vectors for only the smallest input.
|
|
176
176
|
if k_vec_a.size < k_vec_b.size:
|
|
177
|
-
k_vec_a, k_vec_b = k_vec_b, k_vec_a #
|
|
177
|
+
k_vec_a, k_vec_b = k_vec_b, k_vec_a # pragma: no cover
|
|
178
178
|
|
|
179
179
|
k_vec_a = k_vec_a[..., np.newaxis, :] # (...,1,3)
|
|
180
180
|
k_vec_b = _kak_equivalent_vectors(k_vec_b) # (...,192,3)
|
|
@@ -407,8 +407,7 @@ def two_qubit_gate_product_tabulation(
|
|
|
407
407
|
# If all KAK vectors in the mesh have been tabulated, return.
|
|
408
408
|
missing_vec_inds = np.logical_not(tabulated_kak_inds).nonzero()[0]
|
|
409
409
|
|
|
410
|
-
if not np.any(missing_vec_inds):
|
|
411
|
-
# coverage: ignore
|
|
410
|
+
if not np.any(missing_vec_inds): # pragma: no cover
|
|
412
411
|
return TwoQubitGateTabulation(
|
|
413
412
|
base_gate, np.array(kak_vecs), sq_cycles, max_infidelity, summary, ()
|
|
414
413
|
)
|
|
@@ -74,10 +74,10 @@ def test_ignores_2qubit_target():
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
def test_ignore_unsupported_gate():
|
|
77
|
-
class
|
|
77
|
+
class UnsupportedExample(cirq.testing.SingleQubitGate):
|
|
78
78
|
pass
|
|
79
79
|
|
|
80
|
-
c = cirq.Circuit(
|
|
80
|
+
c = cirq.Circuit(UnsupportedExample()(cirq.LineQubit(0)))
|
|
81
81
|
assert_optimizes(optimized=cirq.merge_k_qubit_unitaries(c, k=1), expected=c)
|
|
82
82
|
|
|
83
83
|
|
|
@@ -0,0 +1,177 @@
|
|
|
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
|
+
from typing import Dict, Optional, Set, Tuple, TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
from cirq import circuits, ops
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
import cirq
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_qubit_mapping_first_and_last_moment(
|
|
24
|
+
circuit: 'cirq.AbstractCircuit',
|
|
25
|
+
) -> Dict['cirq.Qid', Tuple[int, int]]:
|
|
26
|
+
"""Computes `(first_moment_idx, last_moment_idx)` tuple for each qubit in the input circuit.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
circuit: An input cirq circuit to analyze.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
A dict mapping each qubit `q` in the input circuit to a tuple of integers
|
|
33
|
+
`(first_moment_idx, last_moment_idx)` where
|
|
34
|
+
- first_moment_idx: Index of leftmost moment containing an operation that acts on `q`.
|
|
35
|
+
- last_moment_idx: Index of rightmost moment containing an operation that acts on `q`.
|
|
36
|
+
"""
|
|
37
|
+
ret = {q: (len(circuit), 0) for q in circuit.all_qubits()}
|
|
38
|
+
for i, moment in enumerate(circuit):
|
|
39
|
+
for q in moment.qubits:
|
|
40
|
+
ret[q] = (min(ret[q][0], i), max(ret[q][1], i))
|
|
41
|
+
return ret
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _is_temp(q: 'cirq.Qid') -> bool:
|
|
45
|
+
return isinstance(q, (ops.CleanQubit, ops.BorrowableQubit))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def map_clean_and_borrowable_qubits(
|
|
49
|
+
circuit: 'cirq.AbstractCircuit', *, qm: Optional['cirq.QubitManager'] = None
|
|
50
|
+
) -> 'cirq.Circuit':
|
|
51
|
+
"""Uses `qm: QubitManager` to map all `CleanQubit`/`BorrowableQubit`s to system qubits.
|
|
52
|
+
|
|
53
|
+
`CleanQubit` and `BorrowableQubit` are internal qubit types that are used as placeholder qubits
|
|
54
|
+
to record a clean / dirty ancilla allocation request.
|
|
55
|
+
|
|
56
|
+
This transformer uses the `QubitManager` provided in the input to:
|
|
57
|
+
- Allocate clean ancilla qubits by delegating to `qm.qalloc` for all `CleanQubit`s.
|
|
58
|
+
- Allocate dirty qubits for all `BorrowableQubit` types via the following two steps:
|
|
59
|
+
1. First analyse the input circuit and check if there are any suitable system qubits
|
|
60
|
+
that can be borrowed, i.e. ones which do not have any overlapping operations
|
|
61
|
+
between circuit[start_index : end_index] where `(start_index, end_index)` is the
|
|
62
|
+
lifespan of temporary borrowable qubit under consideration. If yes, borrow the system
|
|
63
|
+
qubits to replace the temporary `BorrowableQubit`.
|
|
64
|
+
2. If no system qubits can be borrowed, delegate the request to `qm.qborrow`.
|
|
65
|
+
|
|
66
|
+
Notes:
|
|
67
|
+
1. The borrow protocol can be made more efficient by also considering the newly
|
|
68
|
+
allocated clean ancilla qubits in step-1 before delegating to `qm.borrow`, but this
|
|
69
|
+
optimization is left as a future improvement.
|
|
70
|
+
2. As of now, the transformer does not preserve moment structure and defaults to
|
|
71
|
+
inserting all mapped operations in a resulting circuit using EARLIEST strategy. The reason
|
|
72
|
+
is that preserving moment structure forces additional constraints on the qubit allocation
|
|
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 upto the user to force such constraints
|
|
75
|
+
using the qubit manager instead of making it part of the transformer.
|
|
76
|
+
3. However, for borrowable system qubits managed by the transformer, we do not reuse qubits
|
|
77
|
+
within the same moment.
|
|
78
|
+
4. Since this is not implemented using the cirq transformers infrastructure, we currently
|
|
79
|
+
do not support recursive mapping within sub-circuits and that is left as a future TODO.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
circuit: Input `cirq.Circuit` containing temporarily allocated
|
|
83
|
+
`CleanQubit`/`BorrowableQubit`s.
|
|
84
|
+
qm: An instance of `cirq.QubitManager` specifying the strategy to use for allocating /
|
|
85
|
+
/ deallocating new ancilla qubits to replace the temporary qubits.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
An updated `cirq.Circuit` with all `CleanQubit`/`BorrowableQubit` mapped to either existing
|
|
89
|
+
system qubits or new ancilla qubits allocated using the `qm` qubit manager.
|
|
90
|
+
"""
|
|
91
|
+
if qm is None:
|
|
92
|
+
qm = ops.GreedyQubitManager(prefix="ancilla")
|
|
93
|
+
|
|
94
|
+
allocated_qubits = {q for q in circuit.all_qubits() if _is_temp(q)}
|
|
95
|
+
qubits_lifespan = _get_qubit_mapping_first_and_last_moment(circuit)
|
|
96
|
+
all_qubits = frozenset(circuit.all_qubits() - allocated_qubits)
|
|
97
|
+
trivial_map = {q: q for q in all_qubits}
|
|
98
|
+
# `allocated_map` maintains the mapping of all temporary qubits seen so far, mapping each of
|
|
99
|
+
# them to either a newly allocated managed ancilla or an existing borrowed system qubit.
|
|
100
|
+
allocated_map: Dict['cirq.Qid', 'cirq.Qid'] = {}
|
|
101
|
+
to_free: Set['cirq.Qid'] = set()
|
|
102
|
+
last_op_idx = -1
|
|
103
|
+
|
|
104
|
+
def map_func(op: 'cirq.Operation', idx: int) -> 'cirq.OP_TREE':
|
|
105
|
+
nonlocal last_op_idx, to_free
|
|
106
|
+
assert isinstance(qm, ops.QubitManager)
|
|
107
|
+
|
|
108
|
+
for q in sorted(to_free):
|
|
109
|
+
is_managed_qubit = allocated_map[q] not in all_qubits
|
|
110
|
+
if idx > last_op_idx or is_managed_qubit:
|
|
111
|
+
# is_managed_qubit: if `q` is mapped to a newly allocated qubit managed by the qubit
|
|
112
|
+
# manager, we can free it immediately after the previous operation ends. This
|
|
113
|
+
# assumes that a managed qubit is not considered by the transformer as part of
|
|
114
|
+
# borrowing qubits (first point of the notes above).
|
|
115
|
+
# idx > last_op_idx: if `q` is mapped to a system qubit, which is not managed by the
|
|
116
|
+
# qubit manager, we free it only at the end of the moment.
|
|
117
|
+
if is_managed_qubit:
|
|
118
|
+
qm.qfree([allocated_map[q]])
|
|
119
|
+
allocated_map.pop(q)
|
|
120
|
+
to_free.remove(q)
|
|
121
|
+
|
|
122
|
+
last_op_idx = idx
|
|
123
|
+
|
|
124
|
+
# To check borrowable qubits, we manually manage only the original system qubits
|
|
125
|
+
# that are not managed by the qubit manager. If any of the system qubits cannot be
|
|
126
|
+
# borrowed, we defer to the qubit manager to allocate a new clean qubit for us.
|
|
127
|
+
# This is a heuristic and can be improved by also checking if any allocated but not
|
|
128
|
+
# yet freed managed qubit can be borrowed for the shorter scope, but we ignore the
|
|
129
|
+
# optimization for the sake of simplicity here.
|
|
130
|
+
borrowable_qubits = set(all_qubits) - set(allocated_map.values())
|
|
131
|
+
|
|
132
|
+
op_temp_qubits = (q for q in op.qubits if _is_temp(q))
|
|
133
|
+
for q in op_temp_qubits:
|
|
134
|
+
# Get the lifespan of this temporarily allocated ancilla qubit `q`.
|
|
135
|
+
st, en = qubits_lifespan[q]
|
|
136
|
+
assert st <= idx <= en
|
|
137
|
+
if en == idx:
|
|
138
|
+
# Mark that this temporarily allocated qubit can be freed after this moment ends.
|
|
139
|
+
to_free.add(q)
|
|
140
|
+
if q in allocated_map or st < idx:
|
|
141
|
+
# The qubit already has a mapping iff we have seen it before.
|
|
142
|
+
assert st < idx and q in allocated_map
|
|
143
|
+
# This line is actually covered by
|
|
144
|
+
# `test_map_clean_and_borrowable_qubits_deallocates_only_once` but pytest-cov seems
|
|
145
|
+
# to not recognize it and hence the pragma: no cover.
|
|
146
|
+
continue # pragma: no cover
|
|
147
|
+
|
|
148
|
+
# This is the first time we are seeing this temporary qubit and need to find a mapping.
|
|
149
|
+
if isinstance(q, ops.CleanQubit):
|
|
150
|
+
# Allocate a new clean qubit if `q` using the qubit manager.
|
|
151
|
+
allocated_map[q] = qm.qalloc(1)[0]
|
|
152
|
+
elif isinstance(q, ops.BorrowableQubit):
|
|
153
|
+
# For each of the system qubits that can be borrowed, check whether they have a
|
|
154
|
+
# conflicting operation in the range [st, en]; which is the scope for which the
|
|
155
|
+
# borrower needs the borrowed qubit for.
|
|
156
|
+
start_frontier = {q: st for q in borrowable_qubits}
|
|
157
|
+
end_frontier = {q: en + 1 for q in borrowable_qubits}
|
|
158
|
+
ops_in_between = circuit.findall_operations_between(start_frontier, end_frontier)
|
|
159
|
+
# Filter the set of borrowable qubits which do not have any conflicting operations.
|
|
160
|
+
filtered_borrowable_qubits = borrowable_qubits - set(
|
|
161
|
+
q for _, op in ops_in_between for q in op.qubits
|
|
162
|
+
)
|
|
163
|
+
if filtered_borrowable_qubits:
|
|
164
|
+
# Allocate a borrowable qubit and remove it from the pool of available qubits.
|
|
165
|
+
allocated_map[q] = min(filtered_borrowable_qubits)
|
|
166
|
+
borrowable_qubits.remove(allocated_map[q])
|
|
167
|
+
else:
|
|
168
|
+
# Use the qubit manager to get a new borrowable qubit, since we couldn't find
|
|
169
|
+
# one from the original system qubits.
|
|
170
|
+
allocated_map[q] = qm.qborrow(1)[0]
|
|
171
|
+
else:
|
|
172
|
+
assert False, f"Unknown temporary qubit type {q}"
|
|
173
|
+
|
|
174
|
+
# Return the transformed operation / decomposed op-tree.
|
|
175
|
+
return op.transform_qubits({**allocated_map, **trivial_map})
|
|
176
|
+
|
|
177
|
+
return circuits.Circuit(map_func(op, idx) for idx, m in enumerate(circuit) for op in m)
|