From c582a16d8209ce5fda5cd7f751b2d6ed1d5eb7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Thu, 22 Aug 2024 11:06:26 +0200 Subject: [PATCH 1/3] Get rid of encoded assumption in TwoQubitBasisDecomposer that the given KAK gate is a Rust-space StandardGate --- crates/accelerate/src/two_qubit_decompose.rs | 66 +++++++++++++------ ...ser-non-std-kak-gate-edc69ffb5d9ef302.yaml | 5 ++ test/python/synthesis/test_synthesis.py | 9 +++ 3 files changed, 60 insertions(+), 20 deletions(-) create mode 100644 releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs index 2072c5034ff7..77ebb3f9433c 100644 --- a/crates/accelerate/src/two_qubit_decompose.rs +++ b/crates/accelerate/src/two_qubit_decompose.rs @@ -55,6 +55,7 @@ use qiskit_circuit::circuit_data::CircuitData; use qiskit_circuit::circuit_instruction::OperationFromPython; use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE}; use qiskit_circuit::operations::{Param, StandardGate}; +use qiskit_circuit::packed_instruction::PackedOperation; use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex}; use qiskit_circuit::util::{c64, GateArray1Q, GateArray2Q, C_M_ONE, C_ONE, C_ZERO, IM, M_IM}; use qiskit_circuit::Qubit; @@ -2063,26 +2064,51 @@ impl TwoQubitBasisDecomposer { ) -> PyResult { let kak_gate = kak_gate.extract::(py)?; let sequence = self.__call__(unitary, basis_fidelity, approximate, _num_basis_uses)?; - CircuitData::from_standard_gates( - py, - 2, - sequence - .gates - .into_iter() - .map(|(gate, params, qubits)| match gate { - Some(gate) => ( - gate, - params.into_iter().map(Param::Float).collect(), - qubits.into_iter().map(|x| Qubit(x.into())).collect(), - ), - None => ( - kak_gate.operation.standard_gate(), - kak_gate.params.clone(), - qubits.into_iter().map(|x| Qubit(x.into())).collect(), - ), - }), - Param::Float(sequence.global_phase), - ) + match kak_gate.operation.try_standard_gate() { + Some(std_kak_gate) => CircuitData::from_standard_gates( + py, + 2, + sequence + .gates + .into_iter() + .map(|(gate, params, qubits)| match gate { + Some(gate) => ( + gate, + params.into_iter().map(Param::Float).collect(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + ), + None => ( + std_kak_gate, + kak_gate.params.clone(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + ), + }), + Param::Float(sequence.global_phase), + ), + None => CircuitData::from_packed_operations( + py, + 2, + 0, + sequence + .gates + .into_iter() + .map(|(gate, params, qubits)| match gate { + Some(gate) => ( + PackedOperation::from_standard(gate), + params.into_iter().map(Param::Float).collect(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + Vec::new(), + ), + None => ( + kak_gate.operation.clone(), + kak_gate.params.clone(), + qubits.into_iter().map(|x| Qubit(x.into())).collect(), + Vec::new(), + ), + }), + Param::Float(sequence.global_phase), + ), + } } fn num_basis_gates(&self, unitary: PyReadonlyArray2) -> usize { diff --git a/releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml b/releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml new file mode 100644 index 000000000000..903b9ceac2a2 --- /dev/null +++ b/releasenotes/notes/fix-2q-basis-decomposer-non-std-kak-gate-edc69ffb5d9ef302.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Fixed a bug in :class:`.TwoQubitBasisDecomposer` where the Rust-based code + would panic if the given KAK gate wasn't a Rust-space :class:`StandardGate`. \ No newline at end of file diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index 05739db249b7..881d6a77db76 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -1270,6 +1270,15 @@ def test_use_dag(self, euler_bases, kak_gates, seed): requested_basis = set(oneq_gates + [kak_gate_name]) self.assertTrue(decomposition_basis.issubset(requested_basis)) + def test_non_std_gate(self): + """Test that the TwoQubitBasisDecomposer class can be correctly instantiated with a + non-standard gate. + + Reproduce from: https://github.com/Qiskit/qiskit/issues/12998 + """ + # note that `CXGate(ctrl_state=0)` is not handled as a "standard" gate. + TwoQubitBasisDecomposer(CXGate(ctrl_state=0))(CXGate()) + @ddt class TestPulseOptimalDecompose(CheckDecompositions): From 12f76060f95bffee0f333f77a6b5e5bc1f41fe6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= Date: Thu, 22 Aug 2024 14:09:09 +0200 Subject: [PATCH 2/3] Improve unit test --- test/python/synthesis/test_synthesis.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index 881d6a77db76..72354dccf897 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -1272,12 +1272,15 @@ def test_use_dag(self, euler_bases, kak_gates, seed): def test_non_std_gate(self): """Test that the TwoQubitBasisDecomposer class can be correctly instantiated with a - non-standard gate. + non-standard KAK gate. Reproduce from: https://github.com/Qiskit/qiskit/issues/12998 """ # note that `CXGate(ctrl_state=0)` is not handled as a "standard" gate. - TwoQubitBasisDecomposer(CXGate(ctrl_state=0))(CXGate()) + decomposer = TwoQubitBasisDecomposer(CXGate(ctrl_state=0)) + unitary = CXGate() + decomposed_unitary = decomposer(unitary) + self.assertEqual(Operator(unitary), Operator(decomposed_unitary)) @ddt From ae4bfe63d9ad35964c3c0308a09d77cc3e111e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elena=20Pe=C3=B1a=20Tapia?= <57907331+ElePT@users.noreply.github.com> Date: Fri, 23 Aug 2024 08:44:53 +0200 Subject: [PATCH 3/3] Update test/python/synthesis/test_synthesis.py Co-authored-by: Matthew Treinish --- test/python/synthesis/test_synthesis.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/python/synthesis/test_synthesis.py b/test/python/synthesis/test_synthesis.py index 72354dccf897..b4fac3f427f0 100644 --- a/test/python/synthesis/test_synthesis.py +++ b/test/python/synthesis/test_synthesis.py @@ -1278,9 +1278,12 @@ def test_non_std_gate(self): """ # note that `CXGate(ctrl_state=0)` is not handled as a "standard" gate. decomposer = TwoQubitBasisDecomposer(CXGate(ctrl_state=0)) - unitary = CXGate() + unitary = SwapGate().to_matrix() decomposed_unitary = decomposer(unitary) self.assertEqual(Operator(unitary), Operator(decomposed_unitary)) + self.assertNotIn("swap", decomposed_unitary.count_ops()) + self.assertNotIn("cx", decomposed_unitary.count_ops()) + self.assertEqual(3, decomposed_unitary.count_ops()["cx_o0"]) @ddt