Skip to content

Commit

Permalink
Directly construct CircuitData in TwoQubitWeylDecomposition
Browse files Browse the repository at this point in the history
In the recently merged Qiskit#12740 a path was added for constructing the
`CircuitData` in rust when synthesizing to a `QuantumCircuit`
directly. However, in that PR this was done through a layer of
indirection by first collecting the gates into an intermediate `Vec`
and then passing an iterator of that into
`CircuitData::from_standard_gates()`. However this resulted in an
unecessary allocation for two `Vec`s and it would have been better
to just directly construct the `CircuitData` object directly. However,
to accomplish this we needed more rust space methods for creating
and manipulating a `CircuitData` object as it's mostly being constructed
from Python space or using `CircuitData::from_standard_gates()` with
an iterator so far. This commit makes those additions and then updates
the `TwoQubitWeylDecomposition` code to directly construct the
`CircuitData` object instead of using an intermediate `Vec`.
  • Loading branch information
mtreinish committed Jul 24, 2024
1 parent e362da5 commit c416756
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 44 deletions.
54 changes: 27 additions & 27 deletions crates/accelerate/src/two_qubit_decompose.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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(())
}
}

Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -1089,19 +1088,19 @@ 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(
simplify,
&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,
Expand All @@ -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(
Expand All @@ -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)
}
}

Expand Down
72 changes: 55 additions & 17 deletions crates/circuit/src/circuit_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub struct CircuitData {
clbits: BitData<Clbit>,
param_table: ParamTable,
#[pyo3(get)]
global_phase: Param,
pub global_phase: Param,
}

impl CircuitData {
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down

0 comments on commit c416756

Please sign in to comment.