diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index 640eb22b9401..72eaa34d4142 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -953,28 +953,16 @@ impl CircuitData { sequence.py(), array .iter() + .map(|value| Param::Float(*value)) .zip(old_table.drain_ordered()) - .map(|(value, (param_ob, uses))| (param_ob, Param::Float(*value), uses)), + .map(|(value, (obj, uses))| (obj, value, uses)), ) } else { let values = sequence .iter()? .map(|ob| Param::extract_no_coerce(&ob?)) .collect::>>()?; - if values.len() != self.param_table.num_parameters() { - return Err(PyValueError::new_err(concat!( - "Mismatching number of values and parameters. For partial binding ", - "please pass a dictionary of {parameter: value} pairs." - ))); - } - let mut old_table = std::mem::take(&mut self.param_table); - self.assign_parameters_inner( - sequence.py(), - values - .into_iter() - .zip(old_table.drain_ordered()) - .map(|(value, (param_ob, uses))| (param_ob, value, uses)), - ) + self.assign_parameters_from_slice(sequence.py(), &values) } } @@ -1135,6 +1123,50 @@ impl CircuitData { self.data.iter() } + /// Assigns parameters to circuit data based on a slice of `Param`. + pub fn assign_parameters_from_slice(&mut self, py: Python, slice: &[Param]) -> PyResult<()> { + if slice.len() != self.param_table.num_parameters() { + return Err(PyValueError::new_err(concat!( + "Mismatching number of values and parameters. For partial binding ", + "please pass a mapping of {parameter: value} pairs." + ))); + } + let mut old_table = std::mem::take(&mut self.param_table); + self.assign_parameters_inner( + py, + slice + .iter() + .zip(old_table.drain_ordered()) + .map(|(value, (param_ob, uses))| (param_ob, value.clone_ref(py), uses)), + ) + } + + /// Assigns parameters to circuit data based on a mapping of `ParameterUuid` : `Param`. + /// This mapping assumes that the provided `ParameterUuid` keys are instances + /// of `ParameterExpression`. + pub fn assign_parameters_from_mapping(&mut self, py: Python, iter: I) -> PyResult<()> + where + I: IntoIterator, + T: AsRef, + { + let mut items = Vec::new(); + for (param_uuid, value) in iter { + // Assume all the Parameters are already in the circuit + let param_obj = self.get_parameter_by_uuid(param_uuid); + if let Some(param_obj) = param_obj { + // Copy or increase ref_count for Parameter, avoid acquiring the GIL. + items.push(( + param_obj.clone_ref(py), + value.as_ref().clone_ref(py), + self.param_table.pop(param_uuid)?, + )); + } else { + return Err(PyValueError::new_err("An invalid parameter was provided.")); + } + } + self.assign_parameters_inner(py, items) + } + /// Returns an immutable view of the Interner used for Qargs pub fn qargs_interner(&self) -> &Interner<[Qubit]> { &self.qargs_interner @@ -1170,9 +1202,10 @@ impl CircuitData { self.cargs_interner().get(index) } - fn assign_parameters_inner(&mut self, py: Python, iter: I) -> PyResult<()> + fn assign_parameters_inner(&mut self, py: Python, iter: I) -> PyResult<()> where - I: IntoIterator, Param, HashSet)>, + I: IntoIterator, T, HashSet)>, + T: AsRef + Clone, { let inconsistent = || PyRuntimeError::new_err("internal error: circuit parameter table is inconsistent"); @@ -1209,7 +1242,7 @@ impl CircuitData { for (param_ob, value, uses) in iter { debug_assert!(!uses.is_empty()); uuids.clear(); - for inner_param_ob in value.iter_parameters(py)? { + for inner_param_ob in value.as_ref().iter_parameters(py)? { uuids.push(self.param_table.track(&inner_param_ob?, None)?) } for usage in uses { @@ -1220,7 +1253,7 @@ impl CircuitData { }; self.set_global_phase( py, - bind_expr(expr.bind_borrowed(py), ¶m_ob, &value, true)?, + bind_expr(expr.bind_borrowed(py), ¶m_ob, value.as_ref(), true)?, )?; } ParameterUse::Index { @@ -1234,17 +1267,21 @@ impl CircuitData { let Param::ParameterExpression(expr) = ¶ms[parameter] else { return Err(inconsistent()); }; - params[parameter] = - match bind_expr(expr.bind_borrowed(py), ¶m_ob, &value, true)? { - Param::Obj(obj) => { - return Err(CircuitError::new_err(format!( - "bad type after binding for gate '{}': '{}'", - standard.name(), - obj.bind(py).repr()?, - ))) - } - param => param, - }; + params[parameter] = match bind_expr( + expr.bind_borrowed(py), + ¶m_ob, + value.as_ref(), + true, + )? { + Param::Obj(obj) => { + return Err(CircuitError::new_err(format!( + "bad type after binding for gate '{}': '{}'", + standard.name(), + obj.bind(py).repr()?, + ))) + } + param => param, + }; for uuid in uuids.iter() { self.param_table.add_use(*uuid, usage)? } @@ -1264,7 +1301,7 @@ impl CircuitData { user_operations .entry(instruction) .or_insert_with(Vec::new) - .push((param_ob.clone_ref(py), value.clone())); + .push((param_ob.clone_ref(py), value.as_ref().clone_ref(py))); let op = previous.unpack_py_op(py)?.into_bound(py); let previous_param = &previous.params_view()[parameter]; @@ -1276,7 +1313,7 @@ impl CircuitData { let new_param = bind_expr( expr.bind_borrowed(py), ¶m_ob, - &value, + value.as_ref(), false, )?; // Historically, `assign_parameters` called `validate_parameter` @@ -1305,7 +1342,7 @@ impl CircuitData { Param::extract_no_coerce( &obj.call_method( assign_parameters_attr, - ([(¶m_ob, &value)].into_py_dict_bound(py),), + ([(¶m_ob, value.as_ref())].into_py_dict_bound(py),), Some( &[("inplace", false), ("flat_input", true)] .into_py_dict_bound(py), diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index ccf41d7eefb2..f6e087a5680f 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -125,6 +125,24 @@ impl Param { Param::Obj(ob.clone().unbind()) }) } + + /// Clones the [Param] object safely by reference count or copying. + pub fn clone_ref(&self, py: Python) -> Self { + match self { + Param::ParameterExpression(exp) => Param::ParameterExpression(exp.clone_ref(py)), + Param::Float(float) => Param::Float(*float), + Param::Obj(obj) => Param::Obj(obj.clone_ref(py)), + } + } +} + +// This impl allows for shared usage between [Param] and &[Param]. +// Such blanked impl doesn't exist inherently due to Rust's type system limitations. +// See https://doc.rust-lang.org/std/convert/trait.AsRef.html#reflexivity for more information. +impl AsRef for Param { + fn as_ref(&self) -> &Param { + self + } } /// Struct to provide iteration over Python-space `Parameter` instances within a `Param`.