Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move QuantumCircuit.assign_parameters to Rust #12794

Merged
merged 9 commits into from
Aug 1, 2024
658 changes: 385 additions & 273 deletions crates/circuit/src/circuit_data.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/circuit/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub static CONTROLLED_GATE: ImportOnceCell =
pub static DEEPCOPY: ImportOnceCell = ImportOnceCell::new("copy", "deepcopy");
pub static QI_OPERATOR: ImportOnceCell = ImportOnceCell::new("qiskit.quantum_info", "Operator");
pub static WARNINGS_WARN: ImportOnceCell = ImportOnceCell::new("warnings", "warn");
pub static UUID: ImportOnceCell = ImportOnceCell::new("uuid", "UUID");

/// A mapping from the enum variant in crate::operations::StandardGate to the python
/// module path and class name to import it. This is used to populate the conversion table
Expand Down
66 changes: 54 additions & 12 deletions crates/circuit/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use smallvec::smallvec;
use numpy::IntoPyArray;
use numpy::PyReadonlyArray2;
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyTuple};
use pyo3::types::{IntoPyDict, PyFloat, PyIterator, PyTuple};
use pyo3::{intern, IntoPy, Python};

#[derive(Clone, Debug)]
Expand All @@ -37,17 +37,13 @@ pub enum Param {

impl<'py> FromPyObject<'py> for Param {
fn extract_bound(b: &Bound<'py, PyAny>) -> Result<Self, PyErr> {
Ok(
if b.is_instance(PARAMETER_EXPRESSION.get_bound(b.py()))?
|| b.is_instance(QUANTUM_CIRCUIT.get_bound(b.py()))?
{
Param::ParameterExpression(b.clone().unbind())
} else if let Ok(val) = b.extract::<f64>() {
Param::Float(val)
} else {
Param::Obj(b.clone().unbind())
},
)
Ok(if b.is_instance(PARAMETER_EXPRESSION.get_bound(b.py()))? {
Param::ParameterExpression(b.clone().unbind())
} else if let Ok(val) = b.extract::<f64>() {
Param::Float(val)
} else {
Param::Obj(b.clone().unbind())
})
}
}

Expand All @@ -71,6 +67,52 @@ impl ToPyObject for Param {
}
}

impl Param {
/// Get an iterator over any Python-space `Parameter` instances tracked within this `Param`.
pub fn iter_parameters<'py>(&self, py: Python<'py>) -> PyResult<ParamParameterIter<'py>> {
let parameters_attr = intern!(py, "parameters");
match self {
Param::Float(_) => Ok(ParamParameterIter(None)),
Param::ParameterExpression(expr) => Ok(ParamParameterIter(Some(
expr.bind(py).getattr(parameters_attr)?.iter()?,
))),
Param::Obj(obj) => {
let obj = obj.bind(py);
if obj.is_instance(QUANTUM_CIRCUIT.get_bound(py))? {
Ok(ParamParameterIter(Some(
obj.getattr(parameters_attr)?.iter()?,
)))
} else {
Ok(ParamParameterIter(None))
}
}
}
}

/// Extract from a Python object without numeric coercion to float. The default conversion will
/// coerce integers into floats, but in things like `assign_parameters`, this is not always
/// desirable.
pub fn extract_no_coerce(ob: &Bound<PyAny>) -> PyResult<Self> {
Ok(if ob.is_instance_of::<PyFloat>() {
Param::Float(ob.extract()?)
} else if ob.is_instance(PARAMETER_EXPRESSION.get_bound(ob.py()))? {
Param::ParameterExpression(ob.clone().unbind())
} else {
Param::Obj(ob.clone().unbind())
})
}
}

/// Struct to provide iteration over Python-space `Parameter` instances within a `Param`.
pub struct ParamParameterIter<'py>(Option<Bound<'py, PyIterator>>);
impl<'py> Iterator for ParamParameterIter<'py> {
type Item = PyResult<Bound<'py, PyAny>>;

fn next(&mut self) -> Option<Self::Item> {
self.0.as_mut().and_then(|iter| iter.next())
}
}

/// Trait for generic circuit operations these define the common attributes
/// needed for something to be addable to the circuit struct
pub trait Operation {
Expand Down
Loading
Loading