cirq-core 1.1.0.dev20221219200817__py3-none-any.whl → 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cirq/__init__.py +8 -0
- cirq/_compat.py +29 -4
- cirq/_compat_test.py +24 -26
- cirq/_version.py +32 -1
- cirq/_version_test.py +1 -1
- cirq/circuits/_block_diagram_drawer_test.py +4 -3
- cirq/circuits/circuit.py +109 -63
- cirq/circuits/circuit_operation.py +2 -3
- cirq/circuits/circuit_operation_test.py +4 -4
- cirq/circuits/circuit_test.py +11 -0
- cirq/circuits/frozen_circuit.py +13 -1
- cirq/circuits/frozen_circuit_test.py +5 -1
- cirq/circuits/moment.py +39 -14
- cirq/circuits/moment_test.py +7 -0
- cirq/circuits/text_diagram_drawer.py +1 -1
- cirq/circuits/text_diagram_drawer_test.py +3 -7
- cirq/conftest.py +8 -0
- cirq/contrib/acquaintance/bipartite.py +1 -1
- cirq/contrib/acquaintance/devices.py +2 -2
- cirq/contrib/acquaintance/executor.py +5 -2
- cirq/contrib/acquaintance/gates.py +3 -2
- cirq/contrib/acquaintance/permutation.py +13 -2
- cirq/contrib/acquaintance/testing.py +3 -5
- cirq/contrib/paulistring/recombine.py +3 -6
- cirq/contrib/qasm_import/_parser.py +17 -21
- cirq/contrib/qasm_import/_parser_test.py +30 -45
- cirq/contrib/qcircuit/qcircuit_test.py +3 -7
- cirq/contrib/quantum_volume/quantum_volume.py +3 -3
- cirq/contrib/quimb/mps_simulator.py +1 -1
- cirq/contrib/quimb/state_vector.py +2 -0
- cirq/contrib/quirk/quirk_gate.py +1 -0
- cirq/contrib/svg/svg.py +4 -7
- cirq/contrib/svg/svg_test.py +29 -1
- cirq/devices/grid_qubit.py +26 -28
- cirq/devices/grid_qubit_test.py +21 -5
- cirq/devices/line_qubit.py +10 -12
- cirq/devices/line_qubit_test.py +9 -2
- cirq/devices/named_topologies.py +1 -1
- cirq/devices/noise_model.py +4 -1
- cirq/devices/superconducting_qubits_noise_properties.py +1 -3
- cirq/experiments/n_qubit_tomography.py +1 -1
- cirq/experiments/qubit_characterizations.py +2 -2
- cirq/experiments/single_qubit_readout_calibration.py +1 -1
- cirq/experiments/t2_decay_experiment.py +1 -1
- cirq/experiments/xeb_simulation_test.py +2 -2
- cirq/interop/quirk/cells/testing.py +1 -1
- cirq/json_resolver_cache.py +1 -0
- cirq/linalg/__init__.py +2 -0
- cirq/linalg/decompositions_test.py +4 -4
- cirq/linalg/diagonalize_test.py +5 -6
- cirq/linalg/transformations.py +72 -9
- cirq/linalg/transformations_test.py +23 -7
- cirq/ops/__init__.py +4 -0
- cirq/ops/arithmetic_operation.py +4 -6
- cirq/ops/classically_controlled_operation.py +10 -3
- cirq/ops/clifford_gate.py +1 -7
- cirq/ops/common_channels.py +21 -15
- cirq/ops/common_gate_families.py +2 -3
- cirq/ops/common_gates.py +48 -11
- cirq/ops/common_gates_test.py +4 -0
- cirq/ops/controlled_gate.py +44 -18
- cirq/ops/controlled_operation.py +13 -5
- cirq/ops/dense_pauli_string.py +14 -19
- cirq/ops/diagonal_gate.py +3 -4
- cirq/ops/eigen_gate.py +8 -10
- cirq/ops/eigen_gate_test.py +6 -0
- cirq/ops/gate_operation.py +11 -6
- cirq/ops/gate_operation_test.py +11 -2
- cirq/ops/gateset.py +2 -1
- cirq/ops/gateset_test.py +38 -5
- cirq/ops/global_phase_op.py +28 -2
- cirq/ops/global_phase_op_test.py +21 -0
- cirq/ops/identity.py +1 -1
- cirq/ops/kraus_channel_test.py +2 -2
- cirq/ops/linear_combinations.py +7 -6
- cirq/ops/linear_combinations_test.py +26 -10
- cirq/ops/matrix_gates.py +8 -4
- cirq/ops/matrix_gates_test.py +25 -3
- cirq/ops/measure_util.py +13 -5
- cirq/ops/measure_util_test.py +8 -2
- cirq/ops/measurement_gate.py +1 -1
- cirq/ops/measurement_gate_test.py +9 -4
- cirq/ops/mixed_unitary_channel_test.py +4 -4
- cirq/ops/named_qubit.py +2 -4
- cirq/ops/parity_gates.py +5 -1
- cirq/ops/parity_gates_test.py +6 -0
- cirq/ops/pauli_gates.py +9 -9
- cirq/ops/pauli_string.py +4 -2
- cirq/ops/pauli_string_raw_types.py +4 -11
- cirq/ops/pauli_string_test.py +13 -13
- cirq/ops/pauli_sum_exponential.py +6 -1
- cirq/ops/qubit_manager.py +97 -0
- cirq/ops/qubit_manager_test.py +66 -0
- cirq/ops/raw_types.py +75 -33
- cirq/ops/raw_types_test.py +34 -0
- cirq/ops/three_qubit_gates.py +16 -10
- cirq/ops/three_qubit_gates_test.py +4 -2
- cirq/ops/two_qubit_diagonal_gate.py +3 -3
- cirq/ops/wait_gate.py +1 -1
- cirq/protocols/__init__.py +1 -0
- cirq/protocols/act_on_protocol.py +3 -3
- cirq/protocols/act_on_protocol_test.py +5 -5
- cirq/protocols/apply_channel_protocol.py +9 -8
- cirq/protocols/apply_mixture_protocol.py +8 -8
- cirq/protocols/apply_mixture_protocol_test.py +1 -1
- cirq/protocols/apply_unitary_protocol.py +66 -19
- cirq/protocols/apply_unitary_protocol_test.py +50 -0
- cirq/protocols/circuit_diagram_info_protocol.py +7 -9
- cirq/protocols/decompose_protocol.py +167 -125
- cirq/protocols/decompose_protocol_test.py +132 -2
- cirq/protocols/has_stabilizer_effect_protocol.py +2 -1
- cirq/protocols/inverse_protocol.py +2 -2
- cirq/protocols/json_serialization_test.py +3 -3
- cirq/protocols/json_test_data/Linspace.json +20 -7
- cirq/protocols/json_test_data/Linspace.repr +4 -1
- cirq/protocols/json_test_data/Points.json +19 -8
- cirq/protocols/json_test_data/Points.repr +4 -1
- cirq/protocols/json_test_data/Result.repr_inward +1 -1
- cirq/protocols/json_test_data/ResultDict.repr +1 -1
- cirq/protocols/json_test_data/ResultDict.repr_inward +1 -1
- cirq/protocols/json_test_data/TrialResult.repr_inward +1 -1
- cirq/protocols/json_test_data/XPowGate.json +13 -5
- cirq/protocols/json_test_data/XPowGate.repr +1 -1
- cirq/protocols/json_test_data/ZPowGate.json +13 -5
- cirq/protocols/json_test_data/ZPowGate.repr +1 -1
- cirq/protocols/json_test_data/ZipLongest.json +19 -0
- cirq/protocols/json_test_data/ZipLongest.repr +1 -0
- cirq/protocols/json_test_data/spec.py +1 -0
- cirq/protocols/kraus_protocol.py +3 -4
- cirq/protocols/measurement_key_protocol.py +3 -1
- cirq/protocols/mixture_protocol.py +3 -2
- cirq/protocols/phase_protocol.py +3 -3
- cirq/protocols/pow_protocol.py +1 -2
- cirq/protocols/qasm.py +4 -4
- cirq/protocols/qid_shape_protocol.py +8 -8
- cirq/protocols/resolve_parameters.py +8 -3
- cirq/protocols/resolve_parameters_test.py +3 -3
- cirq/protocols/unitary_protocol.py +19 -11
- cirq/protocols/unitary_protocol_test.py +37 -0
- cirq/qis/channels.py +1 -1
- cirq/qis/clifford_tableau.py +4 -5
- cirq/qis/quantum_state_representation.py +7 -9
- cirq/qis/states.py +21 -13
- cirq/qis/states_test.py +7 -0
- cirq/sim/clifford/clifford_simulator.py +3 -3
- cirq/sim/density_matrix_simulation_state.py +2 -1
- cirq/sim/density_matrix_simulator.py +1 -1
- cirq/sim/density_matrix_simulator_test.py +9 -5
- cirq/sim/density_matrix_utils.py +7 -32
- cirq/sim/mux.py +2 -2
- cirq/sim/simulation_state.py +58 -18
- cirq/sim/simulation_state_base.py +5 -2
- cirq/sim/simulation_state_test.py +121 -9
- cirq/sim/simulation_utils.py +59 -0
- cirq/sim/simulation_utils_test.py +32 -0
- cirq/sim/simulator.py +2 -1
- cirq/sim/simulator_base_test.py +3 -3
- cirq/sim/sparse_simulator.py +1 -1
- cirq/sim/sparse_simulator_test.py +5 -5
- cirq/sim/state_vector.py +7 -36
- cirq/sim/state_vector_simulation_state.py +18 -1
- cirq/sim/state_vector_simulator.py +3 -2
- cirq/sim/state_vector_simulator_test.py +24 -2
- cirq/sim/state_vector_test.py +46 -15
- cirq/study/__init__.py +1 -0
- cirq/study/flatten_expressions.py +2 -2
- cirq/study/resolver.py +2 -0
- cirq/study/resolver_test.py +1 -1
- cirq/study/result.py +1 -1
- cirq/study/sweeps.py +103 -9
- cirq/study/sweeps_test.py +64 -0
- cirq/testing/__init__.py +4 -0
- cirq/testing/circuit_compare.py +15 -18
- cirq/testing/consistent_act_on.py +4 -4
- cirq/testing/consistent_controlled_gate_op_test.py +1 -1
- cirq/testing/consistent_decomposition.py +11 -2
- cirq/testing/consistent_decomposition_test.py +8 -1
- cirq/testing/consistent_protocols.py +2 -0
- cirq/testing/consistent_protocols_test.py +8 -4
- cirq/testing/consistent_qasm.py +8 -15
- cirq/testing/consistent_specified_has_unitary.py +1 -1
- cirq/testing/consistent_unitary.py +85 -0
- cirq/testing/consistent_unitary_test.py +96 -0
- cirq/testing/equivalent_repr_eval.py +10 -10
- cirq/testing/json.py +3 -3
- cirq/testing/logs.py +1 -1
- cirq/testing/order_tester.py +4 -5
- cirq/testing/random_circuit.py +3 -5
- cirq/testing/sample_gates.py +79 -0
- cirq/testing/sample_gates_test.py +59 -0
- cirq/transformers/__init__.py +2 -0
- cirq/transformers/analytical_decompositions/__init__.py +8 -0
- cirq/transformers/analytical_decompositions/pauli_string_decomposition.py +130 -0
- cirq/transformers/analytical_decompositions/pauli_string_decomposition_test.py +58 -0
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition.py +230 -0
- cirq/transformers/analytical_decompositions/quantum_shannon_decomposition_test.py +112 -0
- cirq/transformers/analytical_decompositions/three_qubit_decomposition_test.py +1 -3
- cirq/transformers/analytical_decompositions/two_qubit_to_fsim.py +1 -1
- cirq/transformers/expand_composite.py +1 -1
- cirq/transformers/heuristic_decompositions/gate_tabulation_math_utils.py +4 -4
- cirq/transformers/measurement_transformers.py +4 -4
- cirq/transformers/merge_single_qubit_gates.py +17 -4
- cirq/transformers/routing/route_circuit_cqc.py +2 -2
- cirq/transformers/stratify.py +125 -62
- cirq/transformers/stratify_test.py +20 -16
- cirq/transformers/transformer_api.py +1 -1
- cirq/transformers/transformer_primitives.py +3 -2
- cirq/transformers/transformer_primitives_test.py +11 -0
- cirq/value/abc_alt.py +3 -2
- cirq/value/abc_alt_test.py +1 -0
- cirq/value/classical_data.py +10 -10
- cirq/value/digits.py +2 -2
- cirq/value/linear_dict.py +18 -19
- cirq/value/product_state.py +7 -6
- cirq/value/value_equality_attr.py +2 -2
- cirq/vis/heatmap.py +1 -1
- cirq/vis/heatmap_test.py +2 -2
- cirq/work/collector.py +2 -2
- cirq/work/observable_measurement_data.py +5 -5
- cirq/work/observable_readout_calibration.py +3 -1
- cirq/work/observable_settings.py +1 -1
- cirq/work/pauli_sum_collector.py +9 -8
- cirq/work/sampler.py +2 -0
- cirq/work/zeros_sampler.py +2 -2
- {cirq_core-1.1.0.dev20221219200817.dist-info → cirq_core-1.2.0.dist-info}/METADATA +7 -15
- {cirq_core-1.1.0.dev20221219200817.dist-info → cirq_core-1.2.0.dist-info}/RECORD +229 -215
- {cirq_core-1.1.0.dev20221219200817.dist-info → cirq_core-1.2.0.dist-info}/WHEEL +1 -1
- {cirq_core-1.1.0.dev20221219200817.dist-info → cirq_core-1.2.0.dist-info}/LICENSE +0 -0
- {cirq_core-1.1.0.dev20221219200817.dist-info → cirq_core-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
import dataclasses
|
|
15
|
+
|
|
16
|
+
import cirq
|
|
17
|
+
import numpy as np
|
|
18
|
+
from cirq import ops, qis, protocols
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _matrix_for_phasing_state(num_qubits, phase_state, phase):
|
|
22
|
+
matrix = qis.eye_tensor((2,) * num_qubits, dtype=np.complex128)
|
|
23
|
+
matrix = matrix.reshape((2**num_qubits, 2**num_qubits))
|
|
24
|
+
matrix[phase_state, phase_state] = phase
|
|
25
|
+
print(num_qubits, phase_state, phase)
|
|
26
|
+
print(matrix)
|
|
27
|
+
return matrix
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclasses.dataclass(frozen=True)
|
|
31
|
+
class PhaseUsingCleanAncilla(ops.Gate):
|
|
32
|
+
r"""Phases the state $|phase_state>$ by $\exp(1j * \pi * \theta)$ using one clean ancilla."""
|
|
33
|
+
|
|
34
|
+
theta: float
|
|
35
|
+
phase_state: int = 1
|
|
36
|
+
target_bitsize: int = 1
|
|
37
|
+
ancilla_bitsize: int = 1
|
|
38
|
+
|
|
39
|
+
def _num_qubits_(self):
|
|
40
|
+
return self.target_bitsize
|
|
41
|
+
|
|
42
|
+
def _decompose_with_context_(self, qubits, *, context: protocols.DecompositionContext):
|
|
43
|
+
anc = context.qubit_manager.qalloc(self.ancilla_bitsize)
|
|
44
|
+
cv = [int(x) for x in f'{self.phase_state:0{self.target_bitsize}b}']
|
|
45
|
+
cnot_ladder = [cirq.CNOT(anc[i - 1], anc[i]) for i in range(1, self.ancilla_bitsize)]
|
|
46
|
+
|
|
47
|
+
yield ops.X(anc[0]).controlled_by(*qubits, control_values=cv)
|
|
48
|
+
yield [cnot_ladder, ops.Z(anc[-1]) ** self.theta, reversed(cnot_ladder)]
|
|
49
|
+
yield ops.X(anc[0]).controlled_by(*qubits, control_values=cv)
|
|
50
|
+
|
|
51
|
+
def narrow_unitary(self) -> np.ndarray:
|
|
52
|
+
"""Narrowed unitary corresponding to the unitary effect applied on target qubits."""
|
|
53
|
+
phase = np.exp(1j * np.pi * self.theta)
|
|
54
|
+
return _matrix_for_phasing_state(self.target_bitsize, self.phase_state, phase)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclasses.dataclass(frozen=True)
|
|
58
|
+
class PhaseUsingDirtyAncilla(ops.Gate):
|
|
59
|
+
r"""Phases the state $|phase_state>$ by -1 using one dirty ancilla."""
|
|
60
|
+
|
|
61
|
+
phase_state: int = 1
|
|
62
|
+
target_bitsize: int = 1
|
|
63
|
+
ancilla_bitsize: int = 1
|
|
64
|
+
|
|
65
|
+
def _num_qubits_(self):
|
|
66
|
+
return self.target_bitsize
|
|
67
|
+
|
|
68
|
+
def _decompose_with_context_(self, qubits, *, context: protocols.DecompositionContext):
|
|
69
|
+
anc = context.qubit_manager.qalloc(self.ancilla_bitsize)
|
|
70
|
+
cv = [int(x) for x in f'{self.phase_state:0{self.target_bitsize}b}']
|
|
71
|
+
cnot_ladder = [cirq.CNOT(anc[i - 1], anc[i]) for i in range(1, self.ancilla_bitsize)]
|
|
72
|
+
yield ops.X(anc[0]).controlled_by(*qubits, control_values=cv)
|
|
73
|
+
yield [cnot_ladder, ops.Z(anc[-1]), reversed(cnot_ladder)]
|
|
74
|
+
yield ops.X(anc[0]).controlled_by(*qubits, control_values=cv)
|
|
75
|
+
yield [cnot_ladder, ops.Z(anc[-1]), reversed(cnot_ladder)]
|
|
76
|
+
|
|
77
|
+
def narrow_unitary(self) -> np.ndarray:
|
|
78
|
+
"""Narrowed unitary corresponding to the unitary effect applied on target qubits."""
|
|
79
|
+
return _matrix_for_phasing_state(self.target_bitsize, self.phase_state, -1)
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from cirq.testing import sample_gates
|
|
18
|
+
import cirq
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.parametrize('theta', np.linspace(0, 2 * np.pi, 20))
|
|
22
|
+
def test_phase_using_clean_ancilla(theta: float):
|
|
23
|
+
g = sample_gates.PhaseUsingCleanAncilla(theta)
|
|
24
|
+
q = cirq.LineQubit(0)
|
|
25
|
+
qubit_order = cirq.QubitOrder.explicit([q], fallback=cirq.QubitOrder.DEFAULT)
|
|
26
|
+
decomposed_unitary = cirq.Circuit(cirq.decompose_once(g.on(q))).unitary(qubit_order=qubit_order)
|
|
27
|
+
phase = np.exp(1j * np.pi * theta)
|
|
28
|
+
np.testing.assert_allclose(g.narrow_unitary(), np.array([[1, 0], [0, phase]]))
|
|
29
|
+
np.testing.assert_allclose(
|
|
30
|
+
decomposed_unitary,
|
|
31
|
+
# fmt: off
|
|
32
|
+
np.array(
|
|
33
|
+
[
|
|
34
|
+
[1 , 0 , 0 , 0],
|
|
35
|
+
[0 , phase, 0 , 0],
|
|
36
|
+
[0 , 0 , phase, 0],
|
|
37
|
+
[0 , 0 , 0 , 1],
|
|
38
|
+
]
|
|
39
|
+
),
|
|
40
|
+
# fmt: on
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@pytest.mark.parametrize(
|
|
45
|
+
'target_bitsize, phase_state', [(1, 0), (1, 1), (2, 0), (2, 1), (2, 2), (2, 3)]
|
|
46
|
+
)
|
|
47
|
+
@pytest.mark.parametrize('ancilla_bitsize', [1, 4])
|
|
48
|
+
def test_phase_using_dirty_ancilla(target_bitsize, phase_state, ancilla_bitsize):
|
|
49
|
+
g = sample_gates.PhaseUsingDirtyAncilla(phase_state, target_bitsize, ancilla_bitsize)
|
|
50
|
+
q = cirq.LineQubit.range(target_bitsize)
|
|
51
|
+
qubit_order = cirq.QubitOrder.explicit(q, fallback=cirq.QubitOrder.DEFAULT)
|
|
52
|
+
decomposed_circuit = cirq.Circuit(cirq.decompose_once(g.on(*q)))
|
|
53
|
+
decomposed_unitary = decomposed_circuit.unitary(qubit_order=qubit_order)
|
|
54
|
+
phase_matrix = np.eye(2**target_bitsize)
|
|
55
|
+
phase_matrix[phase_state, phase_state] = -1
|
|
56
|
+
np.testing.assert_allclose(g.narrow_unitary(), phase_matrix)
|
|
57
|
+
np.testing.assert_allclose(
|
|
58
|
+
decomposed_unitary, np.kron(phase_matrix, np.eye(2**ancilla_bitsize)), atol=1e-5
|
|
59
|
+
)
|
cirq/transformers/__init__.py
CHANGED
|
@@ -25,6 +25,7 @@ from cirq.transformers.analytical_decompositions import (
|
|
|
25
25
|
parameterized_2q_op_to_sqrt_iswap_operations,
|
|
26
26
|
prepare_two_qubit_state_using_cz,
|
|
27
27
|
prepare_two_qubit_state_using_sqrt_iswap,
|
|
28
|
+
quantum_shannon_decomposition,
|
|
28
29
|
single_qubit_matrix_to_gates,
|
|
29
30
|
single_qubit_matrix_to_pauli_rotations,
|
|
30
31
|
single_qubit_matrix_to_phased_x_z,
|
|
@@ -36,6 +37,7 @@ from cirq.transformers.analytical_decompositions import (
|
|
|
36
37
|
two_qubit_matrix_to_diagonal_and_cz_operations,
|
|
37
38
|
two_qubit_matrix_to_ion_operations,
|
|
38
39
|
two_qubit_matrix_to_sqrt_iswap_operations,
|
|
40
|
+
unitary_to_pauli_string,
|
|
39
41
|
)
|
|
40
42
|
|
|
41
43
|
from cirq.transformers.heuristic_decompositions import (
|
|
@@ -28,6 +28,10 @@ from cirq.transformers.analytical_decompositions.cphase_to_fsim import (
|
|
|
28
28
|
decompose_cphase_into_two_fsim,
|
|
29
29
|
)
|
|
30
30
|
|
|
31
|
+
from cirq.transformers.analytical_decompositions.quantum_shannon_decomposition import (
|
|
32
|
+
quantum_shannon_decomposition,
|
|
33
|
+
)
|
|
34
|
+
|
|
31
35
|
from cirq.transformers.analytical_decompositions.single_qubit_decompositions import (
|
|
32
36
|
is_negligible_turn,
|
|
33
37
|
single_qubit_matrix_to_gates,
|
|
@@ -67,3 +71,7 @@ from cirq.transformers.analytical_decompositions.two_qubit_state_preparation imp
|
|
|
67
71
|
from cirq.transformers.analytical_decompositions.single_to_two_qubit_isometry import (
|
|
68
72
|
two_qubit_matrix_to_cz_isometry,
|
|
69
73
|
)
|
|
74
|
+
|
|
75
|
+
from cirq.transformers.analytical_decompositions.pauli_string_decomposition import (
|
|
76
|
+
unitary_to_pauli_string,
|
|
77
|
+
)
|
|
@@ -0,0 +1,130 @@
|
|
|
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 Optional, Tuple, cast
|
|
16
|
+
|
|
17
|
+
import numpy as np
|
|
18
|
+
import numpy.typing as npt
|
|
19
|
+
|
|
20
|
+
from cirq.ops import DensePauliString
|
|
21
|
+
from cirq import protocols
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _argmax(V: npt.NDArray) -> Tuple[int, float]:
|
|
25
|
+
"""Returns a tuple (index of max number, max number)."""
|
|
26
|
+
V = (V * V.conj()).real
|
|
27
|
+
idx_max = np.argmax(V)
|
|
28
|
+
V[idx_max] = 0
|
|
29
|
+
return cast(int, idx_max), np.max(V)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _validate_decomposition(decomposition: DensePauliString, U: npt.NDArray, eps: float) -> bool:
|
|
33
|
+
"""Returns whether the max absolute value of the elementwise difference is less than eps."""
|
|
34
|
+
got = protocols.unitary(decomposition)
|
|
35
|
+
return np.abs(got - U).max() < eps
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _fast_walsh_hadamard_transform(V: npt.NDArray) -> None:
|
|
39
|
+
"""Fast Walsh–Hadamard Transform of an array."""
|
|
40
|
+
m = len(V)
|
|
41
|
+
n = m.bit_length() - 1
|
|
42
|
+
for h in [2**i for i in range(n)]:
|
|
43
|
+
for i in range(0, m, h * 2):
|
|
44
|
+
for j in range(i, i + h):
|
|
45
|
+
x = V[j]
|
|
46
|
+
y = V[j + h]
|
|
47
|
+
V[j] = x + y
|
|
48
|
+
V[j + h] = x - y
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _conjugate_with_hadamard(U: npt.NDArray) -> npt.NDArray:
|
|
52
|
+
"""Applies HcUH in O(n4^n) instead of O(8^n)."""
|
|
53
|
+
|
|
54
|
+
U = np.copy(U.T)
|
|
55
|
+
for i in range(U.shape[1]):
|
|
56
|
+
_fast_walsh_hadamard_transform(U[:, i])
|
|
57
|
+
U = U.T
|
|
58
|
+
for i in range(U.shape[1]):
|
|
59
|
+
_fast_walsh_hadamard_transform(U[:, i])
|
|
60
|
+
return U
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def unitary_to_pauli_string(U: npt.NDArray, eps: float = 1e-15) -> Optional[DensePauliString]:
|
|
64
|
+
"""Attempts to find a pauli string (with possible phase) equivalent to U up to eps.
|
|
65
|
+
|
|
66
|
+
Based on this answer https://shorturl.at/aA079.
|
|
67
|
+
Let x_mask be the index of the maximum number of the first column of U
|
|
68
|
+
and z_mask be the index of the maximum number of the first column of H†UH
|
|
69
|
+
each of these indicies is n-bits long where U is 2^n x 2^n.
|
|
70
|
+
|
|
71
|
+
These two indices/masks encode in binary the indices of the qubits that
|
|
72
|
+
have I, X, Y, Z acting on them as follows:
|
|
73
|
+
x_mask[i] == 1 and z_mask[i] == 0: X acts on the ith qubit
|
|
74
|
+
x_mask[i] == 0 and z_mask[i] == 1: Z acts on the ith qubit
|
|
75
|
+
x_mask[i] == 1 and z_mask[i] == 1: Y acts on the ith qubit
|
|
76
|
+
x_mask[i] == 0 and z_mask[i] == 0: I acts on the ith qubit
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
U: A square array whose dimension is a power of 2.
|
|
80
|
+
eps: numbers smaller than `eps` are considered zero.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
A DensePauliString of None.
|
|
84
|
+
|
|
85
|
+
Raises:
|
|
86
|
+
ValueError: if U is not square with a power of 2 dimension.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
if len(U.shape) != 2 or U.shape[0] != U.shape[1]:
|
|
90
|
+
raise ValueError(f'Input has a non-square shape {U}')
|
|
91
|
+
n = U.shape[0].bit_length() - 1
|
|
92
|
+
if U.shape[0] != 2**n:
|
|
93
|
+
raise ValueError(f'Input dimension {U.shape[0]} isn\'t a power of 2')
|
|
94
|
+
|
|
95
|
+
x_msk, second_largest = _argmax(U[:, 0])
|
|
96
|
+
if second_largest > eps:
|
|
97
|
+
return None
|
|
98
|
+
U_z = _conjugate_with_hadamard(U)
|
|
99
|
+
z_msk, second_largest = _argmax(U_z[:, 0])
|
|
100
|
+
if second_largest > eps:
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
def select(i):
|
|
104
|
+
"""Returns the gate that acts on the ith qubit."""
|
|
105
|
+
has_x = (x_msk >> i) & 1
|
|
106
|
+
has_z = (z_msk >> i) & 1
|
|
107
|
+
# The mapping is:
|
|
108
|
+
# - has_x and not has_z => X
|
|
109
|
+
# - not has_x and has_z => Z
|
|
110
|
+
# - has_x and has_z => Y
|
|
111
|
+
# - not has_x and not has_z => I
|
|
112
|
+
gate_table = ['IX', 'ZY']
|
|
113
|
+
return gate_table[has_z][has_x]
|
|
114
|
+
|
|
115
|
+
decomposition = DensePauliString(''.join(select(i) for i in reversed(range(n))))
|
|
116
|
+
|
|
117
|
+
guess = protocols.unitary(decomposition)
|
|
118
|
+
if np.abs(guess[x_msk, 0]) < eps:
|
|
119
|
+
return None
|
|
120
|
+
phase = U[x_msk, 0] / guess[x_msk, 0]
|
|
121
|
+
phase /= np.abs(phase) # Make sure |phase| = 1 to avoid rounding issues.
|
|
122
|
+
|
|
123
|
+
decomposition = DensePauliString(
|
|
124
|
+
''.join(select(i) for i in reversed(range(n))), coefficient=phase
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if not _validate_decomposition(decomposition, U, eps):
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
return decomposition
|
|
@@ -0,0 +1,58 @@
|
|
|
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 cast
|
|
16
|
+
import itertools
|
|
17
|
+
import cmath
|
|
18
|
+
import pytest
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from cirq.ops import DensePauliString, T
|
|
23
|
+
from cirq import protocols
|
|
24
|
+
from cirq.transformers.analytical_decompositions import unitary_to_pauli_string
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.parametrize('phase', [cmath.exp(i * 2 * cmath.pi / 5 * 1j) for i in range(5)])
|
|
28
|
+
@pytest.mark.parametrize(
|
|
29
|
+
'pauli_string', [''.join(p) for p in itertools.product(['', 'I', 'X', 'Y', 'Z'], repeat=4)]
|
|
30
|
+
)
|
|
31
|
+
def test_unitary_to_pauli_string(pauli_string: str, phase: complex):
|
|
32
|
+
want = DensePauliString(pauli_string, coefficient=phase)
|
|
33
|
+
got = unitary_to_pauli_string(protocols.unitary(want))
|
|
34
|
+
assert got is not None
|
|
35
|
+
assert np.all(want.pauli_mask == got.pauli_mask)
|
|
36
|
+
assert np.isclose(cast(np.complex128, want.coefficient), cast(np.complex128, got.coefficient))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_unitary_to_pauli_string_non_pauli_input():
|
|
40
|
+
got = unitary_to_pauli_string(protocols.unitary(T))
|
|
41
|
+
assert got is None
|
|
42
|
+
|
|
43
|
+
got = unitary_to_pauli_string(np.array([[1, 0], [1, 0]]))
|
|
44
|
+
assert got is None
|
|
45
|
+
|
|
46
|
+
got = unitary_to_pauli_string(np.array([[1, 1], [0, 2]]))
|
|
47
|
+
assert got is None
|
|
48
|
+
|
|
49
|
+
got = unitary_to_pauli_string(np.array([[0, 0.5], [1, -1]]), eps=1.1)
|
|
50
|
+
assert got is None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_invalid_input():
|
|
54
|
+
with pytest.raises(ValueError, match='Input has a non-square shape.*'):
|
|
55
|
+
_ = unitary_to_pauli_string(np.zeros((2, 3)))
|
|
56
|
+
|
|
57
|
+
with pytest.raises(ValueError, match='Input dimension [0-9]* isn\'t a power of 2'):
|
|
58
|
+
_ = unitary_to_pauli_string(np.zeros((3, 3)))
|
|
@@ -0,0 +1,230 @@
|
|
|
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
|
+
|
|
16
|
+
"""Utility methods for decomposing arbitrary n-qubit (2^n x 2^n) unitary.
|
|
17
|
+
|
|
18
|
+
Based on:
|
|
19
|
+
Synthesis of Quantum Logic Circuits. Tech. rep. 2006,
|
|
20
|
+
https://arxiv.org/abs/quant-ph/0406176
|
|
21
|
+
"""
|
|
22
|
+
from typing import List, Callable, TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
from scipy.linalg import cossin
|
|
25
|
+
|
|
26
|
+
import numpy as np
|
|
27
|
+
|
|
28
|
+
from cirq import ops
|
|
29
|
+
from cirq.linalg import decompositions, predicates
|
|
30
|
+
|
|
31
|
+
if TYPE_CHECKING:
|
|
32
|
+
import cirq
|
|
33
|
+
from cirq.ops import op_tree
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def quantum_shannon_decomposition(qubits: 'List[cirq.Qid]', u: np.ndarray) -> 'op_tree.OpTree':
|
|
37
|
+
"""Decomposes n-qubit unitary into CX/YPow/ZPow/CNOT gates, preserving global phase.
|
|
38
|
+
|
|
39
|
+
The algorithm is described in Shende et al.:
|
|
40
|
+
Synthesis of Quantum Logic Circuits. Tech. rep. 2006,
|
|
41
|
+
https://arxiv.org/abs/quant-ph/0406176
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
qubits: List of qubits in order of significance
|
|
45
|
+
u: Numpy array for unitary matrix representing gate to be decomposed
|
|
46
|
+
|
|
47
|
+
Calls:
|
|
48
|
+
(Base Case)
|
|
49
|
+
1. _single_qubit_decomposition
|
|
50
|
+
OR
|
|
51
|
+
(Recursive Case)
|
|
52
|
+
1. _msb_demuxer
|
|
53
|
+
2. _multiplexed_cossin
|
|
54
|
+
3. _msb_demuxer
|
|
55
|
+
|
|
56
|
+
Yields:
|
|
57
|
+
A single 2-qubit or 1-qubit operations from OP TREE
|
|
58
|
+
composed from the set
|
|
59
|
+
{ CNOT, rz, ry, ZPowGate }
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If the u matrix is non-unitary
|
|
63
|
+
ValueError: If the u matrix is not of shape (2^n,2^n)
|
|
64
|
+
"""
|
|
65
|
+
if not predicates.is_unitary(u): # Check that u is unitary
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Expected input matrix u to be unitary, \
|
|
68
|
+
but it fails cirq.is_unitary check"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
n = u.shape[0]
|
|
72
|
+
if n & (n - 1):
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Expected input matrix u to be a (2^n x 2^n) shaped numpy array, \
|
|
75
|
+
but instead got shape {u.shape}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
if n == 2:
|
|
79
|
+
# Yield a single-qubit decomp if u is 2x2
|
|
80
|
+
yield from _single_qubit_decomposition(qubits[0], u)
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# Perform a cosine-sine (linalg) decomposition on u
|
|
84
|
+
# X = [ u1 , 0 ] [ cos(theta) , -sin(theta) ] [ v1 , 0 ]
|
|
85
|
+
# [ 0 , u2 ] [ sin(theta) , cos(theta) ] [ 0 , v2 ]
|
|
86
|
+
(u1, u2), theta, (v1, v2) = cossin(u, n / 2, n / 2, separate=True)
|
|
87
|
+
|
|
88
|
+
# Yield ops from decomposition of multiplexed v1/v2 part
|
|
89
|
+
yield from _msb_demuxer(qubits, v1, v2)
|
|
90
|
+
|
|
91
|
+
# Observe that middle part looks like Σ_i( Ry(theta_i)⊗|i><i| )
|
|
92
|
+
# Then most significant qubit is Ry multiplexed over all other qubits
|
|
93
|
+
# Yield ops from multiplexed Ry part
|
|
94
|
+
yield from _multiplexed_cossin(qubits, theta, ops.ry)
|
|
95
|
+
|
|
96
|
+
# Yield ops from decomposition of multiplexed u1/u2 part
|
|
97
|
+
yield from _msb_demuxer(qubits, u1, u2)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _single_qubit_decomposition(qubit: 'cirq.Qid', u: np.ndarray) -> 'op_tree.OpTree':
|
|
101
|
+
"""Decomposes single-qubit gate, and returns list of operations, keeping phase invariant.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
qubit: Qubit on which to apply operations
|
|
105
|
+
u: (2 x 2) Numpy array for unitary representing 1-qubit gate to be decomposed
|
|
106
|
+
|
|
107
|
+
Yields:
|
|
108
|
+
A single operation from OP TREE of 3 operations (rz,ry,ZPowGate)
|
|
109
|
+
"""
|
|
110
|
+
# Perform native ZYZ decomposition
|
|
111
|
+
phi_0, phi_1, phi_2 = decompositions.deconstruct_single_qubit_matrix_into_angles(u)
|
|
112
|
+
|
|
113
|
+
# Determine global phase picked up
|
|
114
|
+
phase = np.angle(u[0, 0] / (np.exp(-1j * (phi_0) / 2) * np.cos(phi_1 / 2)))
|
|
115
|
+
|
|
116
|
+
# Append first two operations operations
|
|
117
|
+
yield ops.rz(phi_0).on(qubit)
|
|
118
|
+
yield ops.ry(phi_1).on(qubit)
|
|
119
|
+
|
|
120
|
+
# Append third operation with global phase added
|
|
121
|
+
yield ops.ZPowGate(exponent=phi_2 / np.pi, global_shift=phase / phi_2).on(qubit)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _msb_demuxer(
|
|
125
|
+
demux_qubits: 'List[cirq.Qid]', u1: np.ndarray, u2: np.ndarray
|
|
126
|
+
) -> 'op_tree.OpTree':
|
|
127
|
+
"""Demultiplexes a unitary matrix that is multiplexed in its most-significant-qubit.
|
|
128
|
+
|
|
129
|
+
Decomposition structure:
|
|
130
|
+
[ u1 , 0 ] = [ V , 0 ][ D , 0 ][ W , 0 ]
|
|
131
|
+
[ 0 , u2 ] [ 0 , V ][ 0 , D* ][ 0 , W ]
|
|
132
|
+
|
|
133
|
+
Gives: ( u1 )( u2* ) = ( V )( D^2 )( V* )
|
|
134
|
+
and: W = ( D )( V* )( u2 )
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
demux_qubits: Subset of total qubits involved in this unitary gate
|
|
138
|
+
u1: Upper-left quadrant of total unitary to be decomposed (see diagram)
|
|
139
|
+
u2: Lower-right quadrant of total unitary to be decomposed (see diagram)
|
|
140
|
+
|
|
141
|
+
Calls:
|
|
142
|
+
1. quantum_shannon_decomposition
|
|
143
|
+
2. _multiplexed_cossin
|
|
144
|
+
3. quantum_shannon_decomposition
|
|
145
|
+
|
|
146
|
+
Yields: Single operation from OP TREE of 2-qubit and 1-qubit operations
|
|
147
|
+
"""
|
|
148
|
+
# Perform a diagonalization to find values
|
|
149
|
+
u = u1 @ u2.T.conjugate()
|
|
150
|
+
dsquared, V = np.linalg.eig(u)
|
|
151
|
+
d = np.sqrt(dsquared)
|
|
152
|
+
D = np.diag(d)
|
|
153
|
+
W = D @ V.T.conjugate() @ u2
|
|
154
|
+
|
|
155
|
+
# Last term is given by ( I ⊗ W ), demultiplexed
|
|
156
|
+
# Remove most-significant (demuxed) control-qubit
|
|
157
|
+
# Yield operations for QSD on W
|
|
158
|
+
yield from quantum_shannon_decomposition(demux_qubits[1:], W)
|
|
159
|
+
|
|
160
|
+
# Use complex phase of d_i to give theta_i (so d_i* gives -theta_i)
|
|
161
|
+
# Observe that middle part looks like Σ_i( Rz(theta_i)⊗|i><i| )
|
|
162
|
+
# Yield ops from multiplexed Rz part
|
|
163
|
+
yield from _multiplexed_cossin(demux_qubits, -np.angle(d), ops.rz)
|
|
164
|
+
|
|
165
|
+
# Yield operations for QSD on V
|
|
166
|
+
yield from quantum_shannon_decomposition(demux_qubits[1:], V)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _nth_gray(n: int) -> int:
|
|
170
|
+
# Return the nth Gray Code number
|
|
171
|
+
return n ^ (n >> 1)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _multiplexed_cossin(
|
|
175
|
+
cossin_qubits: 'List[cirq.Qid]', angles: List[float], rot_func: Callable = ops.ry
|
|
176
|
+
) -> 'op_tree.OpTree':
|
|
177
|
+
"""Performs a multiplexed rotation over all qubits in this unitary matrix,
|
|
178
|
+
|
|
179
|
+
Uses ry and rz multiplexing for quantum shannon decomposition
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
cossin_qubits: Subset of total qubits involved in this unitary gate
|
|
183
|
+
angles: List of angles to be multiplexed over for the given type of rotation
|
|
184
|
+
rot_func: Rotation function used for this multiplexing implementation
|
|
185
|
+
(cirq.ry or cirq.rz)
|
|
186
|
+
|
|
187
|
+
Calls:
|
|
188
|
+
No major calls
|
|
189
|
+
|
|
190
|
+
Yields: Single operation from OP TREE from set 1- and 2-qubit gates: {ry,rz,CNOT}
|
|
191
|
+
"""
|
|
192
|
+
# Most significant qubit is main qubit with rotation function applied
|
|
193
|
+
main_qubit = cossin_qubits[0]
|
|
194
|
+
|
|
195
|
+
# All other qubits are control qubits
|
|
196
|
+
control_qubits = cossin_qubits[1:]
|
|
197
|
+
|
|
198
|
+
for j in range(len(angles)):
|
|
199
|
+
# The rotation includes a factor of (-1) for each bit in the Gray Code
|
|
200
|
+
# if the position of that bit is also 1
|
|
201
|
+
# The number of factors of -1 is counted using the 1s in the
|
|
202
|
+
# binary representation of the (gray(j) & i)
|
|
203
|
+
# Here, i gives the index for the angle, and
|
|
204
|
+
# j is the iteration of the decomposition
|
|
205
|
+
rotation = sum(
|
|
206
|
+
-angle if bin(_nth_gray(j) & i).count('1') % 2 else angle
|
|
207
|
+
for i, angle in enumerate(angles)
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Divide by a factor of 2 for each additional select qubit
|
|
211
|
+
# This is due to the halving in the decomposition applied recursively
|
|
212
|
+
rotation = rotation * 2 / len(angles)
|
|
213
|
+
|
|
214
|
+
# The XOR of the this gray code with the next will give the 1 for the bit
|
|
215
|
+
# corresponding to the CNOT select, else 0
|
|
216
|
+
select_string = _nth_gray(j) ^ _nth_gray(j + 1)
|
|
217
|
+
|
|
218
|
+
# Find the index number where the bit is 1
|
|
219
|
+
select_qubit = next(i for i in range(len(angles)) if (select_string >> i & 1))
|
|
220
|
+
|
|
221
|
+
# Negate the value, since we must index starting at most significant qubit
|
|
222
|
+
# Also the final value will overflow, and it should be the MSB,
|
|
223
|
+
# so introduce max function
|
|
224
|
+
select_qubit = max(-select_qubit - 1, -len(control_qubits))
|
|
225
|
+
|
|
226
|
+
# Add a rotation on the main qubit
|
|
227
|
+
yield rot_func(rotation).on(main_qubit)
|
|
228
|
+
|
|
229
|
+
# Add a CNOT from the select qubit to the main qubit
|
|
230
|
+
yield ops.CNOT(control_qubits[select_qubit], main_qubit)
|