Skip to content

Commit

Permalink
Initial: Add rust-native apply_operation_front and `apply_operation…
Browse files Browse the repository at this point in the history
…_back methods.

- These methods allow rust users to add an operation to either the front or the back of the circuit without needing to have valid interned indices for the Qargs/Qargs of said operations.
- Leverage the new methods in the existing python variants of them.
  • Loading branch information
raynelfss committed Sep 12, 2024
1 parent 118a16c commit 8625f74
Showing 1 changed file with 155 additions and 43 deletions.
198 changes: 155 additions & 43 deletions crates/circuit/src/dag_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use std::hash::{Hash, Hasher};

use ahash::RandomState;
use smallvec::SmallVec;

use crate::bit_data::BitData;
use crate::circuit_data::CircuitData;
Expand All @@ -26,7 +27,7 @@ use crate::error::DAGCircuitError;
use crate::imports;
use crate::interner::{Interned, Interner};
use crate::operations::{Operation, OperationRef, Param, PyInstruction, StandardGate};
use crate::packed_instruction::PackedInstruction;
use crate::packed_instruction::{PackedInstruction, PackedOperation};
use crate::rustworkx_core_vnext::isomorphism;
use crate::{BitType, Clbit, Qubit, TupleLikeArg};

Expand Down Expand Up @@ -1681,28 +1682,25 @@ def _format(operand):
let py_op = op.extract::<OperationFromPython>()?;
let qargs = qargs.map(|q| q.value);
let cargs = cargs.map(|c| c.value);
let node = {
let qubits_id = self
.qargs_interner
.insert_owned(self.qubits.map_bits(qargs.iter().flatten())?.collect());
let clbits_id = self
.cargs_interner
.insert_owned(self.clbits.map_bits(cargs.iter().flatten())?.collect());
let instr = PackedInstruction {
op: py_op.operation,
qubits: qubits_id,
clbits: clbits_id,
params: (!py_op.params.is_empty()).then(|| Box::new(py_op.params)),
extra_attrs: py_op.extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op: op.unbind().into(),
};

if check {
self.check_op_addition(py, &instr)?;
}
self.push_back(py, instr)?
};
// Map the qargs to is respective indices
let qarg_indices: Vec<Qubit> = self.qubits.map_bits(qargs.iter().flatten())?.collect();
// Map the qargs to is respective indices
let carg_indices: Vec<Clbit> = self.clbits.map_bits(cargs.iter().flatten())?.collect();

let params = (!py_op.params.is_empty()).then_some(py_op.params);

let node = self.apply_operation_back(
py,
py_op.operation,
&qarg_indices,
&carg_indices,
params,
py_op.extra_attrs,
#[cfg(feature = "cache_pygates")]
Some(op.unbind()),
check,
)?;

self.get_node(py, node)
}
Expand Down Expand Up @@ -1735,28 +1733,24 @@ def _format(operand):
let py_op = op.extract::<OperationFromPython>()?;
let qargs = qargs.map(|q| q.value);
let cargs = cargs.map(|c| c.value);
let node = {
let qubits_id = self
.qargs_interner
.insert_owned(self.qubits.map_bits(qargs.iter().flatten())?.collect());
let clbits_id = self
.cargs_interner
.insert_owned(self.clbits.map_bits(cargs.iter().flatten())?.collect());
let instr = PackedInstruction {
op: py_op.operation,
qubits: qubits_id,
clbits: clbits_id,
params: (!py_op.params.is_empty()).then(|| Box::new(py_op.params)),
extra_attrs: py_op.extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op: op.unbind().into(),
};
// Map the qargs to is respective indices
let qarg_indices: Vec<Qubit> = self.qubits.map_bits(qargs.iter().flatten())?.collect();
// Map the qargs to is respective indices
let carg_indices: Vec<Clbit> = self.clbits.map_bits(cargs.iter().flatten())?.collect();

if check {
self.check_op_addition(py, &instr)?;
}
self.push_front(py, instr)?
};
let params = (!py_op.params.is_empty()).then_some(py_op.params);

let node = self.apply_operation_front(
py,
py_op.operation,
&qarg_indices,
&carg_indices,
params,
py_op.extra_attrs,
#[cfg(feature = "cache_pygates")]
Some(op.unbind()),
check,
)?;

self.get_node(py, node)
}
Expand Down Expand Up @@ -5263,6 +5257,124 @@ impl DAGCircuit {
Ok(new_node)
}


/// Apply a [PackedOperation] to the back of the circuit.
#[allow(clippy::too_many_arguments)]
pub fn apply_operation_back(
&mut self,
py: Python,
op: PackedOperation,
qargs: &[Qubit],
cargs: &[Clbit],
params: Option<SmallVec<[Param; 3]>>,
extra_attrs: Option<Box<ExtraInstructionAttributes>>,
#[cfg(feature = "cache_pygates")] py_op: Option<PyObject>,
check: bool,
) -> PyResult<NodeIndex> {
self.inner_apply_op(
py,
op,
qargs,
cargs,
params,
extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op,
check,
false,
)
}

/// Apply a [PackedOperation] to the front of the circuit.
#[allow(clippy::too_many_arguments)]
pub fn apply_operation_front(
&mut self,
py: Python,
op: PackedOperation,
qargs: &[Qubit],
cargs: &[Clbit],
params: Option<SmallVec<[Param; 3]>>,
extra_attrs: Option<Box<ExtraInstructionAttributes>>,
#[cfg(feature = "cache_pygates")] py_op: Option<PyObject>,
check: bool,
) -> PyResult<NodeIndex> {
self.inner_apply_op(
py,
op,
qargs,
cargs,
params,
extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op,
check,
true,
)
}

#[allow(clippy::too_many_arguments)]
fn inner_apply_op(
&mut self,
py: Python,
op: PackedOperation,
qargs: &[Qubit],
cargs: &[Clbit],
params: Option<SmallVec<[Param; 3]>>,
extra_attrs: Option<Box<ExtraInstructionAttributes>>,
#[cfg(feature = "cache_pygates")] py_op: Option<PyObject>,
check: bool,
front: bool,
) -> PyResult<NodeIndex> {
// Check that all qargs are within an acceptable range
qargs.iter().try_for_each(|qarg| {
if !(0..self.num_qubits() as u32).contains(&qarg.0) {
return Err(PyValueError::new_err(format!(
"Qubit index {} is out of range. This DAGCircuit currently has only {} qubits.",
qarg.0,
self.num_qubits()
)));
}
Ok(())
})?;

// Check that all cargs are within an acceptable range
cargs.iter().try_for_each(|carg| {
if !(0..self.num_clbits() as u32).contains(&carg.0) {
return Err(PyValueError::new_err(format!(
"Clbit index {} is out of range. This DAGCircuit currently has only {} clbits.",
carg.0,
self.num_clbits()
)));
}
Ok(())
})?;

#[cfg(feature = "cache_pygates")]
let py_op = if let Some(py_op) = py_op {
py_op.into()
} else {
OnceCell::new()
};
let packed_instruction = PackedInstruction {
op,
qubits: self.qargs_interner.insert(qargs),
clbits: self.cargs_interner.insert(cargs),
params: params.map(|params: SmallVec<[Param; 3]>| Box::new(params)),
extra_attrs,
#[cfg(feature = "cache_pygates")]
py_op,
};
if check {
self.check_op_addition(py, &packed_instruction)?;
}

if front {
self.push_front(py, packed_instruction)
} else {
self.push_back(py, packed_instruction)
}
}

fn sort_key(&self, node: NodeIndex) -> SortKeyType {
match &self.dag[node] {
NodeType::Operation(packed) => (
Expand Down

0 comments on commit 8625f74

Please sign in to comment.