Skip to content

Commit

Permalink
Fix Rust-space TwoQubitBasisDecomposer for non-standard KAK gate (#…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
ElePT and mtreinish authored Aug 23, 2024
1 parent 6b6efc7 commit a4bf87b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 20 deletions.
66 changes: 46 additions & 20 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2063,26 +2064,51 @@ impl TwoQubitBasisDecomposer {
) -> PyResult<CircuitData> {
let kak_gate = kak_gate.extract::<OperationFromPython>(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<Complex64>) -> usize {
Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
15 changes: 15 additions & 0 deletions test/python/synthesis/test_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,21 @@ 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 KAK gate.
Reproduce from: https://github.com/Qiskit/qiskit/issues/12998
"""
# note that `CXGate(ctrl_state=0)` is not handled as a "standard" gate.
decomposer = TwoQubitBasisDecomposer(CXGate(ctrl_state=0))
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
class TestPulseOptimalDecompose(CheckDecompositions):
Expand Down

0 comments on commit a4bf87b

Please sign in to comment.