From 89ca81e8f579ce9accc354f07cc914e0f33e6138 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Mon, 16 Dec 2024 04:59:29 -0600 Subject: [PATCH 1/7] TwoQubitControlledUDecomposer to _decomposer_2q_from_basis_gates --- .../two_qubit/two_qubit_decompose.py | 4 +- .../passes/synthesis/unitary_synthesis.py | 52 +++++++++++++++---- .../transpiler/test_unitary_synthesis.py | 3 +- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/qiskit/synthesis/two_qubit/two_qubit_decompose.py b/qiskit/synthesis/two_qubit/two_qubit_decompose.py index 79a444e6220c..28e82d870d46 100644 --- a/qiskit/synthesis/two_qubit/two_qubit_decompose.py +++ b/qiskit/synthesis/two_qubit/two_qubit_decompose.py @@ -290,7 +290,9 @@ def __init__(self, rxx_equivalent_gate: Type[Gate]): self.rxx_equivalent_gate = rxx_equivalent_gate self.scale = self._inner_decomposition.scale - def __call__(self, unitary: Operator | np.ndarray, *, atol=DEFAULT_ATOL) -> QuantumCircuit: + def __call__( + self, unitary: Operator | np.ndarray, approximate=False, use_dag=False, atol=DEFAULT_ATOL + ) -> QuantumCircuit: """Returns the Weyl decomposition in circuit form. Args: unitary (Operator or ndarray): :math:`4 \times 4` unitary to synthesize. diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index a2bd044c7341..cb0d4c44733d 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -38,6 +38,7 @@ RXXGate, RZXGate, RZZGate, + RYYGate, ECRGate, RXGate, SXGate, @@ -50,6 +51,10 @@ U3Gate, RYGate, RGate, + CRXGate, + CRYGate, + CRZGate, + CPhaseGate, ) from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit.dagcircuit.dagcircuit import DAGCircuit @@ -62,6 +67,7 @@ from qiskit.synthesis.two_qubit.two_qubit_decompose import ( TwoQubitBasisDecomposer, TwoQubitWeylDecomposition, + TwoQubitControlledUDecomposer, ) from qiskit.transpiler.basepasses import TransformationPass from qiskit.transpiler.coupling import CouplingMap @@ -88,16 +94,32 @@ "u3": U3Gate._standard_gate, "ry": RYGate._standard_gate, "r": RGate._standard_gate, + "rzz": RZZGate._standard_gate, + "ryy": RYYGate._standard_gate, + "rxx": RXXGate._standard_gate, + "rzx": RXXGate._standard_gate, + "cp": CPhaseGate._standard_gate, + "crx": RXXGate._standard_gate, + "cry": RXXGate._standard_gate, + "crz": RXXGate._standard_gate, } +KAK_GATE_PARAM_NAMES = { + "rxx": RXXGate, + "rzz": RZZGate, + "ryy": RYYGate, + "rzx": RZXGate, + "cphase": CPhaseGate, + "crx": CRXGate, + "cry": CRYGate, + "crz": CRZGate, +} KAK_GATE_NAMES = { "cx": CXGate(), "cz": CZGate(), "iswap": iSwapGate(), - "rxx": RXXGate(pi / 2), "ecr": ECRGate(), - "rzx": RZXGate(pi / 4), # typically pi/6 is also available } GateNameToGate = get_standard_gate_name_mapping() @@ -107,7 +129,12 @@ def _choose_kak_gate(basis_gates): """Choose the first available 2q gate to use in the KAK decomposition.""" kak_gate = None kak_gates = set(basis_gates or []).intersection(KAK_GATE_NAMES.keys()) - if kak_gates: + kak_gates_params = set(basis_gates or []).intersection(KAK_GATE_PARAM_NAMES.keys()) + + if kak_gates_params: + kak_gate = KAK_GATE_PARAM_NAMES[kak_gates_params.pop()] + + elif kak_gates: kak_gate = KAK_GATE_NAMES[kak_gates.pop()] return kak_gate @@ -151,14 +178,17 @@ def _decomposer_2q_from_basis_gates(basis_gates, pulse_optimize=None, approximat kak_gate = _choose_kak_gate(basis_gates) euler_basis = _choose_euler_basis(basis_gates) basis_fidelity = approximation_degree or 1.0 - if isinstance(kak_gate, RZXGate): - backup_optimizer = TwoQubitBasisDecomposer( - CXGate(), - basis_fidelity=basis_fidelity, - euler_basis=euler_basis, - pulse_optimize=pulse_optimize, - ) - decomposer2q = XXDecomposer(euler_basis=euler_basis, backup_optimizer=backup_optimizer) + # if isinstance(kak_gate, RZXGate): + # backup_optimizer = TwoQubitBasisDecomposer( + # CXGate(), + # basis_fidelity=basis_fidelity, + # euler_basis=euler_basis, + # pulse_optimize=pulse_optimize, + # ) + # decomposer2q = XXDecomposer(euler_basis=euler_basis, backup_optimizer=backup_optimizer) + + if kak_gate in KAK_GATE_PARAM_NAMES.values(): + decomposer2q = TwoQubitControlledUDecomposer(kak_gate) elif kak_gate is not None: decomposer2q = TwoQubitBasisDecomposer( kak_gate, diff --git a/test/python/transpiler/test_unitary_synthesis.py b/test/python/transpiler/test_unitary_synthesis.py index e763a6206d78..ff38086b8444 100644 --- a/test/python/transpiler/test_unitary_synthesis.py +++ b/test/python/transpiler/test_unitary_synthesis.py @@ -130,7 +130,8 @@ def test_empty_basis_gates(self): @data( ["u3", "cx"], ["u1", "u2", "u3", "cx"], - ["rx", "ry", "rxx"], + ["ry", "rz", "rxx"], + ["ry", "rz", "rzz"], ["rx", "rz", "iswap"], ["u3", "rx", "rz", "cz", "iswap"], ) From 077ecb97d40641f023ee11c60e89596906ced555 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Mon, 16 Dec 2024 06:58:19 -0600 Subject: [PATCH 2/7] update (temporarily) basis gates in test --- test/python/compiler/test_transpiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 851c6817d82f..8d07fc234864 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -1277,7 +1277,7 @@ def test_block_collection_reduces_1q_gate(self, basis_gates, gate_counts): basis_gates=[ ["u3", "cx"], ["rx", "rz", "iswap"], - ["rx", "ry", "rxx"], + ["ry", "rz", "rxx"], ], ) def test_translation_method_synthesis(self, optimization_level, basis_gates): From 18b20f66a54a50b93bf05f3554dfd180c05ab871 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Wed, 18 Dec 2024 04:50:44 -0600 Subject: [PATCH 3/7] minor fix --- qiskit/synthesis/two_qubit/two_qubit_decompose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/synthesis/two_qubit/two_qubit_decompose.py b/qiskit/synthesis/two_qubit/two_qubit_decompose.py index 28e82d870d46..c9430f66bd51 100644 --- a/qiskit/synthesis/two_qubit/two_qubit_decompose.py +++ b/qiskit/synthesis/two_qubit/two_qubit_decompose.py @@ -291,7 +291,7 @@ def __init__(self, rxx_equivalent_gate: Type[Gate]): self.scale = self._inner_decomposition.scale def __call__( - self, unitary: Operator | np.ndarray, approximate=False, use_dag=False, atol=DEFAULT_ATOL + self, unitary: Operator | np.ndarray, approximate=False, use_dag=False, *, atol=DEFAULT_ATOL ) -> QuantumCircuit: """Returns the Weyl decomposition in circuit form. Args: From 967da132f9b5a4631b580269b5fa9eb57907603b Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Thu, 19 Dec 2024 06:49:23 -0600 Subject: [PATCH 4/7] add EulerBasis as a parameter to TwoQubitControlledUDecomposer --- crates/accelerate/src/two_qubit_decompose.rs | 18 ++++++++++++------ .../synthesis/two_qubit/two_qubit_decompose.py | 17 +++++++++++++---- .../passes/synthesis/unitary_synthesis.py | 2 +- test/python/synthesis/test_synthesis.py | 17 +++++++++++------ .../transpiler/test_unitary_synthesis.py | 2 +- 5 files changed, 38 insertions(+), 18 deletions(-) diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index 4410d6f35e07..d811a76d6903 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -2448,6 +2448,7 @@ impl RXXEquivalent { #[pyclass(module = "qiskit._accelerate.two_qubit_decompose", subclass)] pub struct TwoQubitControlledUDecomposer { rxx_equivalent_gate: RXXEquivalent, + euler_basis: EulerBasis, #[pyo3(get)] scale: f64, } @@ -2544,9 +2545,8 @@ impl TwoQubitControlledUDecomposer { let decomposer_inv = TwoQubitWeylDecomposition::new_inner(mat.view(), Some(DEFAULT_FIDELITY), None)?; - let euler_basis = EulerBasis::ZYZ; let mut target_1q_basis_list = EulerBasisSet::new(); - target_1q_basis_list.add_basis(euler_basis); + target_1q_basis_list.add_basis(self.euler_basis); // Express the RXXGate in terms of the user-provided RXXGate equivalent gate. let mut gates = Vec::with_capacity(13); @@ -2687,9 +2687,8 @@ impl TwoQubitControlledUDecomposer { let target_decomposed = TwoQubitWeylDecomposition::new_inner(unitary, Some(DEFAULT_FIDELITY), None)?; - let euler_basis = EulerBasis::ZYZ; let mut target_1q_basis_list = EulerBasisSet::new(); - target_1q_basis_list.add_basis(euler_basis); + target_1q_basis_list.add_basis(self.euler_basis); let c1r = target_decomposed.K1r.view(); let c2r = target_decomposed.K2r.view(); @@ -2751,11 +2750,17 @@ impl TwoQubitControlledUDecomposer { /// Args: /// rxx_equivalent_gate: Gate that is locally equivalent to an :class:`.RXXGate`: /// :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` gate. + /// euler_basis: Basis string to be provided to :class:`.OneQubitEulerDecomposer` + /// for 1Q synthesis. /// Raises: /// QiskitError: If the gate is not locally equivalent to an :class:`.RXXGate`. #[new] - #[pyo3(signature=(rxx_equivalent_gate))] - pub fn new(py: Python, rxx_equivalent_gate: RXXEquivalent) -> PyResult { + #[pyo3(signature=(rxx_equivalent_gate, euler_basis="ZYZ"))] + pub fn new( + py: Python, + rxx_equivalent_gate: RXXEquivalent, + euler_basis: &str, + ) -> PyResult { let atol = DEFAULT_ATOL; let test_angles = [0.2, 0.3, PI2]; @@ -2819,6 +2824,7 @@ impl TwoQubitControlledUDecomposer { Ok(TwoQubitControlledUDecomposer { scale, rxx_equivalent_gate, + euler_basis: EulerBasis::__new__(euler_basis)?, }) } diff --git a/qiskit/synthesis/two_qubit/two_qubit_decompose.py b/qiskit/synthesis/two_qubit/two_qubit_decompose.py index c9430f66bd51..b7908017a8cb 100644 --- a/qiskit/synthesis/two_qubit/two_qubit_decompose.py +++ b/qiskit/synthesis/two_qubit/two_qubit_decompose.py @@ -270,34 +270,43 @@ class TwoQubitControlledUDecomposer: :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` gate that is locally equivalent to an :class:`.RXXGate`.""" - def __init__(self, rxx_equivalent_gate: Type[Gate]): + def __init__(self, rxx_equivalent_gate: Type[Gate], euler_basis: str = "ZYZ"): r"""Initialize the KAK decomposition. Args: rxx_equivalent_gate: Gate that is locally equivalent to an :class:`.RXXGate`: - :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` gate. + :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` gate. + euler_basis: Basis string to be provided to :class:`.OneQubitEulerDecomposer` + for 1Q synthesis. + Valid options are [``'ZYZ'``, ``'ZXZ'``, ``'XYX'``, ``'U'``, ``'U3'``, ``'U1X'``, + ``'PSX'``, ``'ZSX'``, ``'RR'``]. + Raises: QiskitError: If the gate is not locally equivalent to an :class:`.RXXGate`. """ if rxx_equivalent_gate._standard_gate is not None: self._inner_decomposition = two_qubit_decompose.TwoQubitControlledUDecomposer( - rxx_equivalent_gate._standard_gate + rxx_equivalent_gate._standard_gate, euler_basis ) else: self._inner_decomposition = two_qubit_decompose.TwoQubitControlledUDecomposer( - rxx_equivalent_gate + rxx_equivalent_gate, euler_basis ) self.rxx_equivalent_gate = rxx_equivalent_gate self.scale = self._inner_decomposition.scale + self.euler_basis = euler_basis def __call__( self, unitary: Operator | np.ndarray, approximate=False, use_dag=False, *, atol=DEFAULT_ATOL ) -> QuantumCircuit: """Returns the Weyl decomposition in circuit form. + Args: unitary (Operator or ndarray): :math:`4 \times 4` unitary to synthesize. + Returns: QuantumCircuit: Synthesized quantum circuit. + Note: atol is passed to OneQubitEulerDecomposer. """ circ_data = self._inner_decomposition(np.asarray(unitary, dtype=complex), atol) diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index cb0d4c44733d..fb47a29d7a5c 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -188,7 +188,7 @@ def _decomposer_2q_from_basis_gates(basis_gates, pulse_optimize=None, approximat # decomposer2q = XXDecomposer(euler_basis=euler_basis, backup_optimizer=backup_optimizer) if kak_gate in KAK_GATE_PARAM_NAMES.values(): - decomposer2q = TwoQubitControlledUDecomposer(kak_gate) + decomposer2q = TwoQubitControlledUDecomposer(kak_gate, euler_basis) elif kak_gate is not None: decomposer2q = TwoQubitBasisDecomposer( kak_gate, diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index b26a049b5567..b6e845e48a55 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -1426,14 +1426,19 @@ def test_approx_supercontrolled_decompose_phase_3_use_random(self, seed, delta=0 class TestTwoQubitControlledUDecompose(CheckDecompositions): """Test TwoQubitControlledUDecomposer() for exact decompositions and raised exceptions""" - @combine(seed=range(10), name="seed_{seed}") - def test_correct_unitary(self, seed): + @combine( + seed=range(10), + gate=[RXXGate, RYYGate, RZZGate, RZXGate, CPhaseGate, CRZGate, CRXGate, CRYGate], + euler_basis=["ZYZ", "ZXZ"], + name="seed_{seed}", + ) + def test_correct_unitary(self, seed, gate, euler_basis): """Verify unitary for different gates in the decomposition""" unitary = random_unitary(4, seed=seed) - for gate in [RXXGate, RYYGate, RZZGate, RZXGate, CPhaseGate, CRZGate, CRXGate, CRYGate]: - decomposer = TwoQubitControlledUDecomposer(gate) - circ = decomposer(unitary) - self.assertEqual(Operator(unitary), Operator(circ)) + + decomposer = TwoQubitControlledUDecomposer(gate, euler_basis) + circ = decomposer(unitary) + self.assertEqual(Operator(unitary), Operator(circ)) def test_not_rxx_equivalent(self): """Test that an exception is raised if the gate is not equivalent to an RXXGate""" diff --git a/test/python/transpiler/test_unitary_synthesis.py b/test/python/transpiler/test_unitary_synthesis.py index ff38086b8444..0e14fc7a1628 100644 --- a/test/python/transpiler/test_unitary_synthesis.py +++ b/test/python/transpiler/test_unitary_synthesis.py @@ -131,7 +131,7 @@ def test_empty_basis_gates(self): ["u3", "cx"], ["u1", "u2", "u3", "cx"], ["ry", "rz", "rxx"], - ["ry", "rz", "rzz"], + ["rx", "rz", "rzz"], ["rx", "rz", "iswap"], ["u3", "rx", "rz", "cz", "iswap"], ) From 3bb10fd18ed70b38940c5ecfea22aa4a9618ea04 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Sun, 22 Dec 2024 07:08:11 -0600 Subject: [PATCH 5/7] fix global_phase calculation in TwoQubitContolledUDecomposer --- crates/accelerate/src/two_qubit_decompose.rs | 8 ++++---- test/python/synthesis/test_synthesis.py | 17 +++++++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index d811a76d6903..23c32cd9d114 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -2583,14 +2583,14 @@ impl TwoQubitControlledUDecomposer { gates.push((None, smallvec![self.scale * angle], smallvec![0, 1])); if let Some(unitary_k1r) = unitary_k1r { - global_phase += unitary_k1r.global_phase; + global_phase -= unitary_k1r.global_phase; for gate in unitary_k1r.gates.into_iter().rev() { let (inv_gate_name, inv_gate_params) = invert_1q_gate(gate); gates.push((Some(inv_gate_name), inv_gate_params, smallvec![0])); } } if let Some(unitary_k1l) = unitary_k1l { - global_phase += unitary_k1l.global_phase; + global_phase -= unitary_k1l.global_phase; for gate in unitary_k1l.gates.into_iter().rev() { let (inv_gate_name, inv_gate_params) = invert_1q_gate(gate); gates.push((Some(inv_gate_name), inv_gate_params, smallvec![1])); @@ -2727,13 +2727,13 @@ impl TwoQubitControlledUDecomposer { global_phase += gates1.global_phase; if let Some(unitary_c1r) = unitary_c1r { - global_phase -= unitary_c1r.global_phase; + global_phase += unitary_c1r.global_phase; for gate in unitary_c1r.gates.into_iter() { gates1.gates.push((Some(gate.0), gate.1, smallvec![0])); } } if let Some(unitary_c1l) = unitary_c1l { - global_phase -= unitary_c1l.global_phase; + global_phase += unitary_c1l.global_phase; for gate in unitary_c1l.gates.into_iter() { gates1.gates.push((Some(gate.0), gate.1, smallvec![1])); } diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index b6e845e48a55..98e20d21c009 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -1427,9 +1427,22 @@ class TestTwoQubitControlledUDecompose(CheckDecompositions): """Test TwoQubitControlledUDecomposer() for exact decompositions and raised exceptions""" @combine( - seed=range(10), + seed=range(5), gate=[RXXGate, RYYGate, RZZGate, RZXGate, CPhaseGate, CRZGate, CRXGate, CRYGate], - euler_basis=["ZYZ", "ZXZ"], + euler_basis=[ + "ZYZ", + "ZXZ", + "XYX", + "XZX", + "RR", + "U", + "U3", + "U321", + "PSX", + "ZSX", + "ZSXX", + "U1X", + ], name="seed_{seed}", ) def test_correct_unitary(self, seed, gate, euler_basis): From 14c7aacea7b3f0e5be8b9338ad21eecd558bf0c4 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Sun, 22 Dec 2024 08:09:35 -0600 Subject: [PATCH 6/7] add TwoQubitControlledUDecomposer to the docs --- qiskit/synthesis/__init__.py | 4 +++- qiskit/synthesis/two_qubit/two_qubit_decompose.py | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/qiskit/synthesis/__init__.py b/qiskit/synthesis/__init__.py index adea95d4260c..4b29c434b72c 100644 --- a/qiskit/synthesis/__init__.py +++ b/qiskit/synthesis/__init__.py @@ -119,6 +119,7 @@ TwoQubitBasisDecomposer XXDecomposer TwoQubitWeylDecomposition + TwoQubitControlledUDecomposer .. autofunction:: two_qubit_cnot_decompose @@ -147,7 +148,7 @@ Multipliers ----------- -.. autofunction:: multiplier_cumulative_h18 +.. autofunction:: multiplier_cumulative_h18 .. autofunction:: multiplier_qft_r17 """ @@ -200,6 +201,7 @@ TwoQubitBasisDecomposer, two_qubit_cnot_decompose, TwoQubitWeylDecomposition, + TwoQubitControlledUDecomposer, ) from .multi_controlled import ( synth_mcmt_vchain, diff --git a/qiskit/synthesis/two_qubit/two_qubit_decompose.py b/qiskit/synthesis/two_qubit/two_qubit_decompose.py index b7908017a8cb..332752275866 100644 --- a/qiskit/synthesis/two_qubit/two_qubit_decompose.py +++ b/qiskit/synthesis/two_qubit/two_qubit_decompose.py @@ -276,10 +276,13 @@ def __init__(self, rxx_equivalent_gate: Type[Gate], euler_basis: str = "ZYZ"): Args: rxx_equivalent_gate: Gate that is locally equivalent to an :class:`.RXXGate`: :math:`U \sim U_d(\alpha, 0, 0) \sim \text{Ctrl-U}` gate. + Valid options are [:class:`.RZZGate`, :class:`.RXXGate`, :class:`.RYYGate`, + :class:`.RZXGate`, :class:`.CPhaseGate`, :class:`.CRXGate`, :class:`.CRYGate`, + :class:`.CRZGate`]. euler_basis: Basis string to be provided to :class:`.OneQubitEulerDecomposer` for 1Q synthesis. - Valid options are [``'ZYZ'``, ``'ZXZ'``, ``'XYX'``, ``'U'``, ``'U3'``, ``'U1X'``, - ``'PSX'``, ``'ZSX'``, ``'RR'``]. + Valid options are [``'ZYZ'``, ``'ZXZ'``, ``'XYX'``, ``'XZX'``, ``'U'``, ``'U3'``, + ``'U321'``, ``'U1X'``, ``'PSX'``, ``'ZSX'``, ``'ZSXX'``, ``'RR'``]. Raises: QiskitError: If the gate is not locally equivalent to an :class:`.RXXGate`. From eeff4cd23503c0cd9ffbb678286b9c14891c69d7 Mon Sep 17 00:00:00 2001 From: ShellyGarion Date: Sun, 22 Dec 2024 09:10:22 -0600 Subject: [PATCH 7/7] make the choice of kak_gate deterministic --- qiskit/transpiler/passes/synthesis/unitary_synthesis.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py index fb47a29d7a5c..49c9dca1d4fa 100644 --- a/qiskit/transpiler/passes/synthesis/unitary_synthesis.py +++ b/qiskit/transpiler/passes/synthesis/unitary_synthesis.py @@ -128,14 +128,14 @@ def _choose_kak_gate(basis_gates): """Choose the first available 2q gate to use in the KAK decomposition.""" kak_gate = None - kak_gates = set(basis_gates or []).intersection(KAK_GATE_NAMES.keys()) - kak_gates_params = set(basis_gates or []).intersection(KAK_GATE_PARAM_NAMES.keys()) + kak_gates = sorted(set(basis_gates or []).intersection(KAK_GATE_NAMES.keys())) + kak_gates_params = sorted(set(basis_gates or []).intersection(KAK_GATE_PARAM_NAMES.keys())) if kak_gates_params: - kak_gate = KAK_GATE_PARAM_NAMES[kak_gates_params.pop()] + kak_gate = KAK_GATE_PARAM_NAMES[kak_gates_params[0]] elif kak_gates: - kak_gate = KAK_GATE_NAMES[kak_gates.pop()] + kak_gate = KAK_GATE_NAMES[kak_gates[0]] return kak_gate