Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 136 additions & 47 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,13 @@ use rand::prelude::*;
use rand_distr::StandardNormal;
use rand_pcg::Pcg64Mcg;

use qiskit_circuit::circuit_data::CircuitData;
use qiskit_circuit::circuit_instruction::convert_py_to_operation_type;
use qiskit_circuit::gate_matrix::{CX_GATE, H_GATE, ONE_QUBIT_IDENTITY, SX_GATE, X_GATE};
use qiskit_circuit::operations::Operation;
use qiskit_circuit::operations::{Operation, Param, StandardGate};
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;

const PI2: f64 = PI / 2.;
const PI4: f64 = PI / 4.;
Expand Down Expand Up @@ -309,10 +312,10 @@ fn compute_unitary(sequence: &TwoQubitSequenceVec, global_phase: f64) -> Array2<
// sequence. If we get a different gate this is getting called
// by something else and is invalid.
let gate_matrix = match inst.0.as_ref() {
"sx" => aview2(&SX_GATE).to_owned(),
"rz" => rz_matrix(inst.1[0]),
"cx" => aview2(&CX_GATE).to_owned(),
"x" => aview2(&X_GATE).to_owned(),
Some(StandardGate::SXGate) => aview2(&SX_GATE).to_owned(),
Some(StandardGate::RZGate) => rz_matrix(inst.1[0]),
Some(StandardGate::CXGate) => aview2(&CX_GATE).to_owned(),
Some(StandardGate::XGate) => aview2(&X_GATE).to_owned(),
_ => unreachable!("Undefined gate"),
};
(gate_matrix, &inst.2)
Expand Down Expand Up @@ -395,6 +398,8 @@ impl Specialization {
}
}

type WeylCircuitSequence = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>;

#[derive(Clone, Debug)]
#[allow(non_snake_case)]
#[pyclass(module = "qiskit._accelerate.two_qubit_decompose", subclass)]
Expand Down Expand Up @@ -425,33 +430,53 @@ impl TwoQubitWeylDecomposition {
fn weyl_gate(
&self,
simplify: bool,
sequence: &mut TwoQubitSequenceVec,
sequence: &mut WeylCircuitSequence,
atol: f64,
global_phase: &mut f64,
) {
match self.specialization {
Specialization::MirrorControlledEquiv => {
sequence.push(("swap".to_string(), SmallVec::new(), smallvec![0, 1]));
sequence.push((
"rzz".to_string(),
smallvec![(PI4 - self.c) * 2.],
smallvec![0, 1],
StandardGate::SwapGate,
SmallVec::new(),
smallvec![Qubit(0), Qubit(1)],
));
sequence.push((
StandardGate::RZZGate,
smallvec![Param::Float((PI4 - self.c) * 2.)],
smallvec![Qubit(0), Qubit(1)],
));
*global_phase += PI4
}
Comment on lines 430 to 450

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind too much, but is there anything stopping us from skipping the WeylCircuitSequence intermediate allocations, and just directly building a CircuitData?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really, I think we could use CircuitData directly here. I don't remember what my thinking was exactly on using the intermediate type here. Maybe I just wanted to use CircuitData::from_standard_gates()?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can always just look at that in a follow-up.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this locally, but let's do this in a follow-up because to do it well I needed to add helper methods to CircuitData to make it easier to manipulate from rust space (unless I missed methods to do the same already) and think it would be good to review those interfaces in isolation (I'm not keen on the clbit interner usage I have there):

diff --git a/crates/accelerate/src/two_qubit_decompose.rs b/crates/accelerate/src/two_qubit_decompose.rs
index ed4d32bd3..3a0c395ac 100644
--- a/crates/accelerate/src/two_qubit_decompose.rs
+++ b/crates/accelerate/src/two_qubit_decompose.rs
@@ -398,8 +398,6 @@ impl Specialization {
     }
 }
 
-type WeylCircuitSequence = Vec<(StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>;
-
 #[derive(Clone, Debug)]
 #[allow(non_snake_case)]
 #[pyclass(module = "qiskit._accelerate.two_qubit_decompose", subclass)]
@@ -430,56 +428,57 @@ impl TwoQubitWeylDecomposition {
     fn weyl_gate(
         &self,
         simplify: bool,
-        sequence: &mut WeylCircuitSequence,
+        sequence: &mut CircuitData,
         atol: f64,
         global_phase: &mut f64,
-    ) {
+    ) -> PyResult<()> {
         match self.specialization {
             Specialization::MirrorControlledEquiv => {
-                sequence.push((
+                sequence.push_standard_gate(
                     StandardGate::SwapGate,
                     SmallVec::new(),
                     smallvec![Qubit(0), Qubit(1)],
-                ));
-                sequence.push((
+                )?;
+                sequence.push_standard_gate(
                     StandardGate::RZZGate,
                     smallvec![Param::Float((PI4 - self.c) * 2.)],
                     smallvec![Qubit(0), Qubit(1)],
-                ));
+                )?;
                 *global_phase += PI4
             }
             Specialization::SWAPEquiv => {
-                sequence.push((
+                sequence.push_standard_gate(
                     StandardGate::SwapGate,
                     SmallVec::new(),
                     smallvec![Qubit(0), Qubit(1)],
-                ));
+                )?;
                 *global_phase -= 3. * PI / 4.
             }
             _ => {
                 if !simplify || self.a.abs() > atol {
-                    sequence.push((
+                    sequence.push_standard_gate(
                         StandardGate::RXXGate,
                         smallvec![Param::Float(-self.a * 2.)],
                         smallvec![Qubit(0), Qubit(1)],
-                    ));
+                    )?;
                 }
                 if !simplify || self.b.abs() > atol {
-                    sequence.push((
+                    sequence.push_standard_gate(
                         StandardGate::RYYGate,
                         smallvec![Param::Float(-self.b * 2.)],
                         smallvec![Qubit(0), Qubit(1)],
-                    ));
+                    )?;
                 }
                 if !simplify || self.c.abs() > atol {
-                    sequence.push((
+                    sequence.push_standard_gate(
                         StandardGate::RZZGate,
                         smallvec![Param::Float(-self.c * 2.)],
                         smallvec![Qubit(0), Qubit(1)],
-                    ));
+                    )?;
                 }
             }
         }
+        Ok(())
     }
 }
 
@@ -1059,7 +1058,7 @@ impl TwoQubitWeylDecomposition {
         };
         let target_1q_basis_list: Vec<EulerBasis> = vec![euler_basis];
 
-        let mut gate_sequence: WeylCircuitSequence = Vec::with_capacity(21);
+        let mut gate_sequence = CircuitData::with_capacity(py, 2, 21, Param::Float(0.))?;
         let mut global_phase: f64 = self.global_phase;
 
         let c2r = unitary_to_gate_sequence_inner(
@@ -1072,11 +1071,11 @@ impl TwoQubitWeylDecomposition {
         )
         .unwrap();
         for gate in c2r.gates {
-            gate_sequence.push((
+            gate_sequence.push_standard_gate(
                 gate.0,
                 gate.1.into_iter().map(Param::Float).collect(),
                 smallvec![Qubit(0)],
-            ))
+            )?
         }
         global_phase += c2r.global_phase;
         let c2l = unitary_to_gate_sequence_inner(
@@ -1089,11 +1088,11 @@ impl TwoQubitWeylDecomposition {
         )
         .unwrap();
         for gate in c2l.gates {
-            gate_sequence.push((
+            gate_sequence.push_standard_gate(
                 gate.0,
                 gate.1.into_iter().map(Param::Float).collect(),
                 smallvec![Qubit(1)],
-            ))
+            )?
         }
         global_phase += c2l.global_phase;
         self.weyl_gate(
@@ -1101,7 +1100,7 @@ impl TwoQubitWeylDecomposition {
             &mut gate_sequence,
             atol.unwrap_or(ANGLE_ZERO_EPSILON),
             &mut global_phase,
-        );
+        )?;
         let c1r = unitary_to_gate_sequence_inner(
             self.K1r.view(),
             &target_1q_basis_list,
@@ -1112,11 +1111,11 @@ impl TwoQubitWeylDecomposition {
         )
         .unwrap();
         for gate in c1r.gates {
-            gate_sequence.push((
+            gate_sequence.push_standard_gate(
                 gate.0,
                 gate.1.into_iter().map(Param::Float).collect(),
                 smallvec![Qubit(0)],
-            ))
+            )?
         }
         global_phase += c2r.global_phase;
         let c1l = unitary_to_gate_sequence_inner(
@@ -1129,13 +1128,14 @@ impl TwoQubitWeylDecomposition {
         )
         .unwrap();
         for gate in c1l.gates {
-            gate_sequence.push((
+            gate_sequence.push_standard_gate(
                 gate.0,
                 gate.1.into_iter().map(Param::Float).collect(),
                 smallvec![Qubit(1)],
-            ))
+            )?
         }
-        CircuitData::from_standard_gates(py, 2, gate_sequence, Param::Float(global_phase))
+        gate_sequence.global_phase = Param::Float(global_phase);
+        Ok(gate_sequence)
     }
 }
 
diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs
index a325ca4e1..2822fef75 100644
--- a/crates/circuit/src/circuit_data.rs
+++ b/crates/circuit/src/circuit_data.rs
@@ -96,7 +96,7 @@ pub struct CircuitData {
     clbits: BitData<Clbit>,
     param_table: ParamTable,
     #[pyo3(get)]
-    global_phase: Param,
+    pub global_phase: Param,
 }
 
 impl CircuitData {
@@ -127,22 +127,8 @@ impl CircuitData {
         I: IntoIterator<Item = (StandardGate, SmallVec<[Param; 3]>, SmallVec<[Qubit; 2]>)>,
     {
         let instruction_iter = instructions.into_iter();
-        let mut res = CircuitData {
-            data: Vec::with_capacity(instruction_iter.size_hint().0),
-            qargs_interner: IndexedInterner::new(),
-            cargs_interner: IndexedInterner::new(),
-            qubits: BitData::new(py, "qubits".to_string()),
-            clbits: BitData::new(py, "clbits".to_string()),
-            param_table: ParamTable::new(),
-            global_phase,
-        };
-        if num_qubits > 0 {
-            let qubit_cls = QUBIT.get_bound(py);
-            for _i in 0..num_qubits {
-                let bit = qubit_cls.call0()?;
-                res.add_qubit(py, &bit, true)?;
-            }
-        }
+        let mut res =
+            Self::with_capacity(py, num_qubits, instruction_iter.size_hint().0, global_phase)?;
         let no_clbit_index = (&mut res.cargs_interner)
             .intern(InternerKey::Value(Vec::new()))?
             .index;
@@ -164,6 +150,58 @@ impl CircuitData {
         Ok(res)
     }
 
+    /// Build an empty CircuitData object with an initially allocated instruction capacity
+    pub fn with_capacity(
+        py: Python,
+        num_qubits: u32,
+        instruction_capacity: usize,
+        global_phase: Param,
+    ) -> PyResult<Self> {
+        let mut res = CircuitData {
+            data: Vec::with_capacity(instruction_capacity),
+            qargs_interner: IndexedInterner::new(),
+            cargs_interner: IndexedInterner::new(),
+            qubits: BitData::new(py, "qubits".to_string()),
+            clbits: BitData::new(py, "clbits".to_string()),
+            param_table: ParamTable::new(),
+            global_phase,
+        };
+        if num_qubits > 0 {
+            let qubit_cls = QUBIT.get_bound(py);
+            for _i in 0..num_qubits {
+                let bit = qubit_cls.call0()?;
+                res.add_qubit(py, &bit, true)?;
+            }
+        }
+        Ok(res)
+    }
+
+    /// Append a standard gate to this CircuitData
+    pub fn push_standard_gate(
+        &mut self,
+        operation: StandardGate,
+        params: SmallVec<[Param; 3]>,
+        qargs: SmallVec<[Qubit; 2]>,
+    ) -> PyResult<()> {
+        let no_clbit_index = (&mut self.cargs_interner)
+            .intern(InternerKey::Value(Vec::new()))?
+            .index;
+        let params = (!params.is_empty()).then(|| Box::new(params));
+        let qubits = (&mut self.qargs_interner)
+            .intern(InternerKey::Value(qargs.to_vec()))?
+            .index;
+        self.data.push(PackedInstruction {
+            op: operation.into(),
+            qubits,
+            clbits: no_clbit_index,
+            params,
+            extra_attrs: None,
+            #[cfg(feature = "cache_pygates")]
+            py_op: RefCell::new(None),
+        });
+        Ok(())
+    }
+
     fn handle_manual_params(
         &mut self,
         py: Python,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let's follow up. I've tagged for merge.

Specialization::SWAPEquiv => {
sequence.push(("swap".to_string(), SmallVec::new(), smallvec![0, 1]));
sequence.push((
StandardGate::SwapGate,
SmallVec::new(),
smallvec![Qubit(0), Qubit(1)],
));
*global_phase -= 3. * PI / 4.
}
_ => {
if !simplify || self.a.abs() > atol {
sequence.push(("rxx".to_string(), smallvec![-self.a * 2.], smallvec![0, 1]));
sequence.push((
StandardGate::RXXGate,
smallvec![Param::Float(-self.a * 2.)],
smallvec![Qubit(0), Qubit(1)],
));
}
if !simplify || self.b.abs() > atol {
sequence.push(("ryy".to_string(), smallvec![-self.b * 2.], smallvec![0, 1]));
sequence.push((
StandardGate::RYYGate,
smallvec![Param::Float(-self.b * 2.)],
smallvec![Qubit(0), Qubit(1)],
));
}
if !simplify || self.c.abs() > atol {
sequence.push(("rzz".to_string(), smallvec![-self.c * 2.], smallvec![0, 1]));
sequence.push((
StandardGate::RZZGate,
smallvec![Param::Float(-self.c * 2.)],
smallvec![Qubit(0), Qubit(1)],
));
}
}
}
Expand Down Expand Up @@ -1023,17 +1048,18 @@ impl TwoQubitWeylDecomposition {
#[pyo3(signature = (euler_basis=None, simplify=false, atol=None))]
fn circuit(
&self,
py: Python,
euler_basis: Option<PyBackedStr>,
simplify: bool,
atol: Option<f64>,
) -> PyResult<TwoQubitGateSequence> {
) -> PyResult<CircuitData> {
let euler_basis: EulerBasis = match euler_basis {
Some(basis) => EulerBasis::__new__(basis.deref())?,
None => self.default_euler_basis,
};
let target_1q_basis_list: Vec<EulerBasis> = vec![euler_basis];

let mut gate_sequence = Vec::new();
let mut gate_sequence: WeylCircuitSequence = Vec::with_capacity(21);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the capacity specifically 21?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An arbitrary 2q decomposition is 3 CX plus local operations, which with single-rotation Eulers is up to 3 gates per local per qubit. Naively, this can run to 27 gates (3x CX interspersed into 4x 6 1q local operations), but maybe there's a way through the decomposition that guarantees that the last set of 6 is never needed? I don't have the maths off the top of my head.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I counted it out as 21 for the two qubit decomposer: https://github.com/Qiskit/qiskit/blob/main/crates/accelerate/src/two_qubit_decompose.rs#L1836-L1842 (but might have miscounted) and was just reusing that number here.

let mut global_phase: f64 = self.global_phase;

let c2r = unitary_to_gate_sequence_inner(
Expand All @@ -1046,7 +1072,11 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c2r.gates {
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0]))
gate_sequence.push((
gate.0,
gate.1.into_iter().map(Param::Float).collect(),
smallvec![Qubit(0)],
))
}
global_phase += c2r.global_phase;
let c2l = unitary_to_gate_sequence_inner(
Expand All @@ -1059,7 +1089,11 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c2l.gates {
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1]))
gate_sequence.push((
gate.0,
gate.1.into_iter().map(Param::Float).collect(),
smallvec![Qubit(1)],
))
}
global_phase += c2l.global_phase;
self.weyl_gate(
Expand All @@ -1078,7 +1112,11 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c1r.gates {
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![0]))
gate_sequence.push((
gate.0,
gate.1.into_iter().map(Param::Float).collect(),
smallvec![Qubit(0)],
))
}
global_phase += c2r.global_phase;
let c1l = unitary_to_gate_sequence_inner(
Expand All @@ -1091,16 +1129,17 @@ impl TwoQubitWeylDecomposition {
)
.unwrap();
for gate in c1l.gates {
gate_sequence.push((gate.0.name().to_string(), gate.1, smallvec![1]))
gate_sequence.push((
gate.0,
gate.1.into_iter().map(Param::Float).collect(),
smallvec![Qubit(1)],
))
}
Ok(TwoQubitGateSequence {
gates: gate_sequence,
global_phase,
})
CircuitData::from_standard_gates(py, 2, gate_sequence, Param::Float(global_phase))
}
}

type TwoQubitSequenceVec = Vec<(String, SmallVec<[f64; 3]>, SmallVec<[u8; 2]>)>;
type TwoQubitSequenceVec = Vec<(Option<StandardGate>, SmallVec<[f64; 3]>, SmallVec<[u8; 2]>)>;

#[pyclass(sequence)]
pub struct TwoQubitGateSequence {
Expand Down Expand Up @@ -1263,17 +1302,21 @@ impl TwoQubitBasisDecomposer {
let mut euler_matrix_q1 = rz_matrix(euler_q1[0][1]).dot(&rx_matrix(euler_q1[0][0]));
euler_matrix_q1 = rx_matrix(euler_q1[0][2] + euler_q1[1][0]).dot(&euler_matrix_q1);
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.view(), 1);
gates.push(("cx".to_string(), smallvec![], smallvec![0, 1]));
gates.push(("sx".to_string(), smallvec![], smallvec![0]));
gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![0, 1]));
gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0]));
gates.push((
"rz".to_string(),
Some(StandardGate::RZGate),
smallvec![euler_q0[1][1] - PI],
smallvec![0],
));
gates.push(("sx".to_string(), smallvec![], smallvec![0]));
gates.push(("rz".to_string(), smallvec![euler_q1[1][1]], smallvec![1]));
gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0]));
gates.push((
Some(StandardGate::RZGate),
smallvec![euler_q1[1][1]],
smallvec![1],
));
global_phase += PI2;
gates.push(("cx".to_string(), smallvec![], smallvec![0, 1]));
gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![0, 1]));
let mut euler_matrix_q0 =
rx_matrix(euler_q0[2][1]).dot(&rz_matrix(euler_q0[1][2] + euler_q0[2][0] + PI2));
euler_matrix_q0 = rz_matrix(euler_q0[2][2]).dot(&euler_matrix_q0);
Expand Down Expand Up @@ -1358,22 +1401,30 @@ impl TwoQubitBasisDecomposer {
euler_matrix_q1 = aview2(&H_GATE).dot(&euler_matrix_q1);
self.append_1q_sequence(&mut gates, &mut global_phase, euler_matrix_q1.view(), 1);

gates.push(("cx".to_string(), smallvec![], smallvec![1, 0]));
gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0]));

if x12_is_pi_mult {
// even or odd multiple
if x12_is_non_zero {
global_phase += x12_phase;
}
if x12_is_non_zero && x12_is_old_mult.unwrap() {
gates.push(("rz".to_string(), smallvec![-euler_q0[1][1]], smallvec![0]));
gates.push((
Some(StandardGate::RZGate),
smallvec![-euler_q0[1][1]],
smallvec![0],
));
} else {
gates.push(("rz".to_string(), smallvec![euler_q0[1][1]], smallvec![0]));
gates.push((
Some(StandardGate::RZGate),
smallvec![euler_q0[1][1]],
smallvec![0],
));
global_phase += PI;
}
}
if x12_is_half_pi {
gates.push(("sx".to_string(), smallvec![], smallvec![0]));
gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![0]));
global_phase -= PI4;
} else if x12_is_non_zero && !x12_is_pi_mult {
if self.pulse_optimize.is_none() {
Expand All @@ -1383,7 +1434,7 @@ impl TwoQubitBasisDecomposer {
}
}
if abs_diff_eq!(euler_q1[1][1], PI2, epsilon = atol) {
gates.push(("sx".to_string(), smallvec![], smallvec![1]));
gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![1]));
global_phase -= PI4
} else if self.pulse_optimize.is_none() {
self.append_1q_sequence(
Expand All @@ -1396,14 +1447,18 @@ impl TwoQubitBasisDecomposer {
return None;
}
gates.push((
"rz".to_string(),
Some(StandardGate::RZGate),
smallvec![euler_q1[1][2] + euler_q1[2][0]],
smallvec![1],
));
gates.push(("cx".to_string(), smallvec![], smallvec![1, 0]));
gates.push(("rz".to_string(), smallvec![euler_q0[2][1]], smallvec![0]));
gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0]));
gates.push((
Some(StandardGate::RZGate),
smallvec![euler_q0[2][1]],
smallvec![0],
));
if abs_diff_eq!(euler_q1[2][1], PI2, epsilon = atol) {
gates.push(("sx".to_string(), smallvec![], smallvec![1]));
gates.push((Some(StandardGate::SXGate), smallvec![], smallvec![1]));
global_phase -= PI4;
} else if self.pulse_optimize.is_none() {
self.append_1q_sequence(
Expand All @@ -1415,7 +1470,7 @@ impl TwoQubitBasisDecomposer {
} else {
return None;
}
gates.push(("cx".to_string(), smallvec![], smallvec![1, 0]));
gates.push((Some(StandardGate::CXGate), smallvec![], smallvec![1, 0]));
let mut euler_matrix = rz_matrix(euler_q0[2][2] + euler_q0[3][0]).dot(&aview2(&H_GATE));
euler_matrix = rx_matrix(euler_q0[3][1]).dot(&euler_matrix);
euler_matrix = rz_matrix(euler_q0[3][2]).dot(&euler_matrix);
Expand Down Expand Up @@ -1460,7 +1515,7 @@ impl TwoQubitBasisDecomposer {
if let Some(sequence) = sequence {
*global_phase += sequence.global_phase;
for gate in sequence.gates {
gates.push((gate.0.name().to_string(), gate.1, smallvec![qubit]));
gates.push((Some(gate.0), gate.1, smallvec![qubit]));
}
}
}
Expand Down Expand Up @@ -1848,27 +1903,27 @@ impl TwoQubitBasisDecomposer {
for i in 0..best_nbasis as usize {
if let Some(euler_decomp) = &euler_decompositions[2 * i] {
for gate in &euler_decomp.gates {
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0]));
gates.push((Some(gate.0), gate.1.clone(), smallvec![0]));
}
global_phase += euler_decomp.global_phase
}
if let Some(euler_decomp) = &euler_decompositions[2 * i + 1] {
for gate in &euler_decomp.gates {
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1]));
gates.push((Some(gate.0), gate.1.clone(), smallvec![1]));
}
global_phase += euler_decomp.global_phase
}
gates.push((self.gate.clone(), smallvec![], smallvec![0, 1]));
gates.push((None, smallvec![], smallvec![0, 1]));
}
if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize] {
for gate in &euler_decomp.gates {
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![0]));
gates.push((Some(gate.0), gate.1.clone(), smallvec![0]));
}
global_phase += euler_decomp.global_phase
}
if let Some(euler_decomp) = &euler_decompositions[2 * best_nbasis as usize + 1] {
for gate in &euler_decomp.gates {
gates.push((gate.0.name().to_string(), gate.1.clone(), smallvec![1]));
gates.push((Some(gate.0), gate.1.clone(), smallvec![1]));
}
global_phase += euler_decomp.global_phase
}
Expand All @@ -1878,6 +1933,40 @@ impl TwoQubitBasisDecomposer {
})
}

#[pyo3(signature = (unitary, kak_gate, basis_fidelity=None, approximate=true, _num_basis_uses=None))]
fn to_circuit(
&self,
py: Python,
unitary: PyReadonlyArray2<Complex64>,
kak_gate: PyObject,
basis_fidelity: Option<f64>,
approximate: bool,
_num_basis_uses: Option<u8>,
) -> PyResult<CircuitData> {
let kak_gate = convert_py_to_operation_type(py, kak_gate)?;
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().unwrap(),
kak_gate.params.clone(),
qubits.into_iter().map(|x| Qubit(x.into())).collect(),
),
}),
Param::Float(sequence.global_phase),
)
}

fn num_basis_gates(&self, unitary: PyReadonlyArray2<Complex64>) -> usize {
_num_basis_gates(self.basis_decomposer.b, self.basis_fidelity, unitary)
}
Expand Down
Loading