Skip to content

Commit a4bf87b

Browse files
ElePTmtreinish
andauthored
Fix Rust-space TwoQubitBasisDecomposer for non-standard KAK gate (#13014)
* Get rid of encoded assumption in TwoQubitBasisDecomposer that the given KAK gate is a Rust-space StandardGate * Improve unit test * Update test/python/synthesis/test_synthesis.py Co-authored-by: Matthew Treinish <mtreinish@kortar.org> --------- Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
1 parent 6b6efc7 commit a4bf87b

File tree

3 files changed

+66
-20
lines changed

3 files changed

+66
-20
lines changed

crates/accelerate/src/two_qubit_decompose.rs

+46-20
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ use qiskit_circuit::circuit_data::CircuitData;
5555
use qiskit_circuit::circuit_instruction::OperationFromPython;
5656
use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE};
5757
use qiskit_circuit::operations::{Param, StandardGate};
58+
use qiskit_circuit::packed_instruction::PackedOperation;
5859
use qiskit_circuit::slice::{PySequenceIndex, SequenceIndex};
5960
use qiskit_circuit::util::{c64, GateArray1Q, GateArray2Q, C_M_ONE, C_ONE, C_ZERO, IM, M_IM};
6061
use qiskit_circuit::Qubit;
@@ -2063,26 +2064,51 @@ impl TwoQubitBasisDecomposer {
20632064
) -> PyResult<CircuitData> {
20642065
let kak_gate = kak_gate.extract::<OperationFromPython>(py)?;
20652066
let sequence = self.__call__(unitary, basis_fidelity, approximate, _num_basis_uses)?;
2066-
CircuitData::from_standard_gates(
2067-
py,
2068-
2,
2069-
sequence
2070-
.gates
2071-
.into_iter()
2072-
.map(|(gate, params, qubits)| match gate {
2073-
Some(gate) => (
2074-
gate,
2075-
params.into_iter().map(Param::Float).collect(),
2076-
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2077-
),
2078-
None => (
2079-
kak_gate.operation.standard_gate(),
2080-
kak_gate.params.clone(),
2081-
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2082-
),
2083-
}),
2084-
Param::Float(sequence.global_phase),
2085-
)
2067+
match kak_gate.operation.try_standard_gate() {
2068+
Some(std_kak_gate) => CircuitData::from_standard_gates(
2069+
py,
2070+
2,
2071+
sequence
2072+
.gates
2073+
.into_iter()
2074+
.map(|(gate, params, qubits)| match gate {
2075+
Some(gate) => (
2076+
gate,
2077+
params.into_iter().map(Param::Float).collect(),
2078+
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2079+
),
2080+
None => (
2081+
std_kak_gate,
2082+
kak_gate.params.clone(),
2083+
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2084+
),
2085+
}),
2086+
Param::Float(sequence.global_phase),
2087+
),
2088+
None => CircuitData::from_packed_operations(
2089+
py,
2090+
2,
2091+
0,
2092+
sequence
2093+
.gates
2094+
.into_iter()
2095+
.map(|(gate, params, qubits)| match gate {
2096+
Some(gate) => (
2097+
PackedOperation::from_standard(gate),
2098+
params.into_iter().map(Param::Float).collect(),
2099+
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2100+
Vec::new(),
2101+
),
2102+
None => (
2103+
kak_gate.operation.clone(),
2104+
kak_gate.params.clone(),
2105+
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
2106+
Vec::new(),
2107+
),
2108+
}),
2109+
Param::Float(sequence.global_phase),
2110+
),
2111+
}
20862112
}
20872113

20882114
fn num_basis_gates(&self, unitary: PyReadonlyArray2<Complex64>) -> usize {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
fixes:
3+
- |
4+
Fixed a bug in :class:`.TwoQubitBasisDecomposer` where the Rust-based code
5+
would panic if the given KAK gate wasn't a Rust-space :class:`StandardGate`.

test/python/synthesis/test_synthesis.py

+15
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,21 @@ def test_use_dag(self, euler_bases, kak_gates, seed):
12701270
requested_basis = set(oneq_gates + [kak_gate_name])
12711271
self.assertTrue(decomposition_basis.issubset(requested_basis))
12721272

1273+
def test_non_std_gate(self):
1274+
"""Test that the TwoQubitBasisDecomposer class can be correctly instantiated with a
1275+
non-standard KAK gate.
1276+
1277+
Reproduce from: https://github.com/Qiskit/qiskit/issues/12998
1278+
"""
1279+
# note that `CXGate(ctrl_state=0)` is not handled as a "standard" gate.
1280+
decomposer = TwoQubitBasisDecomposer(CXGate(ctrl_state=0))
1281+
unitary = SwapGate().to_matrix()
1282+
decomposed_unitary = decomposer(unitary)
1283+
self.assertEqual(Operator(unitary), Operator(decomposed_unitary))
1284+
self.assertNotIn("swap", decomposed_unitary.count_ops())
1285+
self.assertNotIn("cx", decomposed_unitary.count_ops())
1286+
self.assertEqual(3, decomposed_unitary.count_ops()["cx_o0"])
1287+
12731288

12741289
@ddt
12751290
class TestPulseOptimalDecompose(CheckDecompositions):

0 commit comments

Comments
 (0)