From 919a3b9b6386353e7369816486d94fabca8dddb1 Mon Sep 17 00:00:00 2001 From: Kevin Hartman Date: Tue, 10 Sep 2024 18:04:38 -0400 Subject: [PATCH] Move Option> into ExtraInstructionAttributes. Previously, CircuitInstruction and PackedInstruction held the ExtraInstructionAttributes struct within an Option>. By putting the Option> inside ExtraInstructionAttributes, we can use the struct itself to manage its memory and provide access to the attributes behind a nicer interface. The size of ExtraInstructionAttributes should be the same size as the old Option>, so this should not have memory implications. --- crates/accelerate/src/commutation_analysis.rs | 4 +- crates/accelerate/src/commutation_checker.rs | 20 +-- crates/circuit/src/circuit_data.rs | 17 +- crates/circuit/src/circuit_instruction.rs | 169 ++++++++++++------ crates/circuit/src/dag_circuit.rs | 111 +++--------- crates/circuit/src/dag_node.rs | 43 +---- crates/circuit/src/operations.rs | 28 ++- crates/circuit/src/packed_instruction.rs | 12 +- 8 files changed, 177 insertions(+), 227 deletions(-) diff --git a/crates/accelerate/src/commutation_analysis.rs b/crates/accelerate/src/commutation_analysis.rs index 08fa1dda5ec9..ccf354ee99b7 100644 --- a/crates/accelerate/src/commutation_analysis.rs +++ b/crates/accelerate/src/commutation_analysis.rs @@ -95,12 +95,12 @@ fn analyze_commutations_inner( py, &op1, params1, - packed_inst0.extra_attrs.as_deref(), + &packed_inst0.extra_attrs, qargs1, cargs1, &op2, params2, - packed_inst1.extra_attrs.as_deref(), + &packed_inst1.extra_attrs, qargs2, cargs2, MAX_NUM_QUBITS, diff --git a/crates/accelerate/src/commutation_checker.rs b/crates/accelerate/src/commutation_checker.rs index b00d7c624c92..637f4ab15ea1 100644 --- a/crates/accelerate/src/commutation_checker.rs +++ b/crates/accelerate/src/commutation_checker.rs @@ -121,12 +121,12 @@ impl CommutationChecker { py, &op1.instruction.operation.view(), &op1.instruction.params, - op1.instruction.extra_attrs.as_deref(), + &op1.instruction.extra_attrs, &qargs1, &cargs1, &op2.instruction.operation.view(), &op2.instruction.params, - op2.instruction.extra_attrs.as_deref(), + &op2.instruction.extra_attrs, &qargs2, &cargs2, max_num_qubits, @@ -162,12 +162,12 @@ impl CommutationChecker { py, &op1.operation.view(), &op1.params, - op1.extra_attrs.as_deref(), + &op1.extra_attrs, &qargs1, &cargs1, &op2.operation.view(), &op2.params, - op2.extra_attrs.as_deref(), + &op2.extra_attrs, &qargs2, &cargs2, max_num_qubits, @@ -232,12 +232,12 @@ impl CommutationChecker { py: Python, op1: &OperationRef, params1: &[Param], - attrs1: Option<&ExtraInstructionAttributes>, + attrs1: &ExtraInstructionAttributes, qargs1: &[Qubit], cargs1: &[Clbit], op2: &OperationRef, params2: &[Param], - attrs2: Option<&ExtraInstructionAttributes>, + attrs2: &ExtraInstructionAttributes, qargs2: &[Qubit], cargs2: &[Clbit], max_num_qubits: u32, @@ -494,20 +494,20 @@ impl CommutationChecker { fn commutation_precheck( op1: &OperationRef, params1: &[Param], - attrs1: Option<&ExtraInstructionAttributes>, + attrs1: &ExtraInstructionAttributes, qargs1: &[Qubit], cargs1: &[Clbit], op2: &OperationRef, params2: &[Param], - attrs2: Option<&ExtraInstructionAttributes>, + attrs2: &ExtraInstructionAttributes, qargs2: &[Qubit], cargs2: &[Clbit], max_num_qubits: u32, ) -> Option { if op1.control_flow() || op2.control_flow() - || attrs1.is_some_and(|attr| attr.condition.is_some()) - || attrs2.is_some_and(|attr| attr.condition.is_some()) + || attrs1.condition().is_some() + || attrs2.condition().is_some() { return Some(false); } diff --git a/crates/circuit/src/circuit_data.rs b/crates/circuit/src/circuit_data.rs index da49f9edc605..677f33c17038 100644 --- a/crates/circuit/src/circuit_data.rs +++ b/crates/circuit/src/circuit_data.rs @@ -14,7 +14,9 @@ use std::cell::OnceCell; use crate::bit_data::BitData; -use crate::circuit_instruction::{CircuitInstruction, OperationFromPython}; +use crate::circuit_instruction::{ + CircuitInstruction, ExtraInstructionAttributes, OperationFromPython, +}; use crate::imports::{ANNOTATED_OPERATION, CLBIT, QUANTUM_CIRCUIT, QUBIT}; use crate::interner::{Interned, Interner}; use crate::operations::{Operation, OperationRef, Param, StandardGate}; @@ -157,7 +159,7 @@ impl CircuitData { qubits, clbits, params, - extra_attrs: None, + extra_attrs: ExtraInstructionAttributes::default(), #[cfg(feature = "cache_pygates")] py_op: OnceCell::new(), }); @@ -209,7 +211,7 @@ impl CircuitData { qubits, clbits: no_clbit_index, params, - extra_attrs: None, + extra_attrs: ExtraInstructionAttributes::default(), #[cfg(feature = "cache_pygates")] py_op: OnceCell::new(), }); @@ -267,7 +269,7 @@ impl CircuitData { qubits, clbits: no_clbit_index, params, - extra_attrs: None, + extra_attrs: ExtraInstructionAttributes::default(), #[cfg(feature = "cache_pygates")] py_op: OnceCell::new(), }); @@ -626,12 +628,7 @@ impl CircuitData { #[pyo3(signature = (func))] pub fn map_nonstandard_ops(&mut self, py: Python<'_>, func: &Bound) -> PyResult<()> { for inst in self.data.iter_mut() { - if inst.op.try_standard_gate().is_some() - && !inst - .extra_attrs - .as_ref() - .is_some_and(|attrs| attrs.condition.is_some()) - { + if inst.op.try_standard_gate().is_some() && inst.extra_attrs.condition().is_none() { continue; } let py_op = func.call1((inst.unpack_py_op(py)?,))?; diff --git a/crates/circuit/src/circuit_instruction.rs b/crates/circuit/src/circuit_instruction.rs index 463f3352aa92..38a4ed9e7b83 100644 --- a/crates/circuit/src/circuit_instruction.rs +++ b/crates/circuit/src/circuit_instruction.rs @@ -30,16 +30,21 @@ use crate::operations::{ }; use crate::packed_instruction::PackedOperation; -/// These are extra mutable attributes for a circuit instruction's state. In general we don't -/// typically deal with this in rust space and the majority of the time they're not used in Python -/// space either. To save memory these are put in a separate struct and are stored inside a -/// `Box` on `CircuitInstruction` and `PackedInstruction`. #[derive(Debug, Clone)] +struct ExtraAttrs { + label: Option, + duration: Option, + unit: Option, + condition: Option, +} + +/// Extra mutable attributes for a circuit instruction's state. In general we don't +/// typically deal with this in rust space and the majority of the time they're not used in Python +/// space either. To save memory, the attributes are stored inside a `Box` internally, so this +/// struct is no larger than that. +#[derive(Default, Debug, Clone)] pub struct ExtraInstructionAttributes { - pub label: Option, - pub duration: Option, - pub unit: Option, - pub condition: Option, + attributes: Option>, } impl ExtraInstructionAttributes { @@ -51,25 +56,36 @@ impl ExtraInstructionAttributes { duration: Option>, unit: Option, condition: Option>, - ) -> Option { - if label.is_some() || duration.is_some() || unit.is_some() || condition.is_some() { - Some(Self { - label, - duration, - unit, - condition, - }) - } else { - None + ) -> Self { + ExtraInstructionAttributes { + attributes: if label.is_some() + || duration.is_some() + || unit.is_some() + || condition.is_some() + { + Some(Box::new(ExtraAttrs { + label, + duration, + unit, + condition, + })) + } else { + None + }, } } /// Get the Python-space version of the stored `unit`. This evalutes the Python-space default /// (`"dt"`) value if we're storing a `None`. pub fn py_unit(&self, py: Python) -> Py { - self.unit + self.attributes .as_deref() - .map(|unit| <&str as IntoPy>>::into_py(unit, py)) + .and_then(|attrs| { + attrs + .unit + .as_deref() + .map(|unit| <&str as IntoPy>>::into_py(unit, py)) + }) .unwrap_or_else(|| Self::default_unit(py).clone().unbind()) } @@ -77,6 +93,74 @@ impl ExtraInstructionAttributes { pub fn default_unit(py: Python) -> &Bound { intern!(py, "dt") } + + pub fn label(&self) -> Option<&str> { + self.attributes + .as_deref() + .and_then(|attrs| attrs.label.as_deref()) + } + + pub fn set_label(&mut self, label: Option) { + if let Some(attrs) = &mut self.attributes { + attrs.label = label; + self.shrink_if_empty(); + return; + } + if label.is_some() { + self.attributes = Some(Box::new(ExtraAttrs { + label, + duration: None, + unit: None, + condition: None, + })) + } + } + + pub fn duration(&self) -> Option<&PyObject> { + self.attributes + .as_deref() + .and_then(|attrs| attrs.duration.as_ref()) + } + + pub fn unit(&self) -> Option<&str> { + self.attributes + .as_deref() + .and_then(|attrs| attrs.unit.as_deref()) + } + + pub fn condition(&self) -> Option<&PyObject> { + self.attributes + .as_deref() + .and_then(|attrs| attrs.condition.as_ref()) + } + + pub fn set_condition(&mut self, condition: Option) { + if let Some(attrs) = &mut self.attributes { + attrs.condition = condition; + self.shrink_if_empty(); + return; + } + if condition.is_some() { + self.attributes = Some(Box::new(ExtraAttrs { + label: None, + duration: None, + unit: None, + condition, + })) + } + } + + fn shrink_if_empty(&mut self) { + if let Some(attrs) = &self.attributes { + if attrs.label.is_none() + && attrs.duration.is_none() + && attrs.unit.is_none() + && attrs.condition.is_none() + { + self.attributes = None; + } + } + } } /// A single instruction in a :class:`.QuantumCircuit`, comprised of the :attr:`operation` and @@ -122,7 +206,7 @@ pub struct CircuitInstruction { #[pyo3(get)] pub clbits: Py, pub params: SmallVec<[Param; 3]>, - pub extra_attrs: Option>, + pub extra_attrs: ExtraInstructionAttributes, #[cfg(feature = "cache_pygates")] pub py_op: OnceCell>, } @@ -146,9 +230,7 @@ impl CircuitInstruction { } pub fn condition(&self) -> Option<&PyObject> { - self.extra_attrs - .as_ref() - .and_then(|args| args.condition.as_ref()) + self.extra_attrs.condition() } } @@ -189,14 +271,7 @@ impl CircuitInstruction { qubits: as_tuple(py, qubits)?.unbind(), clbits: PyTuple::empty_bound(py).unbind(), params, - extra_attrs: label.map(|label| { - Box::new(ExtraInstructionAttributes { - label: Some(label), - duration: None, - unit: None, - condition: None, - }) - }), + extra_attrs: ExtraInstructionAttributes::new(label, None, None, None), #[cfg(feature = "cache_pygates")] py_op: OnceCell::new(), }) @@ -227,7 +302,7 @@ impl CircuitInstruction { let out = match self.operation.view() { OperationRef::Standard(standard) => standard - .create_py_op(py, Some(&self.params), self.extra_attrs.as_deref())? + .create_py_op(py, Some(&self.params), &self.extra_attrs)? .into_any(), OperationRef::Gate(gate) => gate.gate.clone_ref(py), OperationRef::Instruction(instruction) => instruction.instruction.clone_ref(py), @@ -261,37 +336,24 @@ impl CircuitInstruction { #[getter] fn label(&self) -> Option<&str> { - self.extra_attrs - .as_ref() - .and_then(|attrs| attrs.label.as_deref()) + self.extra_attrs.label() } #[getter] fn get_condition(&self, py: Python) -> Option { - self.extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref().map(|x| x.clone_ref(py))) + self.extra_attrs.condition().map(|x| x.clone_ref(py)) } #[getter] fn duration(&self, py: Python) -> Option { - self.extra_attrs - .as_ref() - .and_then(|attrs| attrs.duration.as_ref().map(|x| x.clone_ref(py))) + self.extra_attrs.duration().map(|x| x.clone_ref(py)) } #[getter] fn unit(&self, py: Python) -> Py { // Python space uses `"dt"` as the default, whereas we simply don't store the extra // attributes at all if they're none. - self.extra_attrs - .as_ref() - .map(|attrs| attrs.py_unit(py)) - .unwrap_or_else(|| { - ExtraInstructionAttributes::default_unit(py) - .clone() - .unbind() - }) + self.extra_attrs.py_unit(py) } /// Is the :class:`.Operation` contained in this instruction a Qiskit standard gate? @@ -524,7 +586,7 @@ impl CircuitInstruction { pub struct OperationFromPython { pub operation: PackedOperation, pub params: SmallVec<[Param; 3]>, - pub extra_attrs: Option>, + pub extra_attrs: ExtraInstructionAttributes, } impl<'py> FromPyObject<'py> for OperationFromPython { @@ -558,8 +620,7 @@ impl<'py> FromPyObject<'py> for OperationFromPython { ob.getattr(intern!(py, "duration"))?.extract()?, unit, ob.getattr(intern!(py, "condition"))?.extract()?, - ) - .map(Box::from)) + )) }; 'standard: { @@ -640,7 +701,7 @@ impl<'py> FromPyObject<'py> for OperationFromPython { return Ok(OperationFromPython { operation: PackedOperation::from_operation(operation), params, - extra_attrs: None, + extra_attrs: ExtraInstructionAttributes::default(), }); } Err(PyTypeError::new_err(format!("invalid input: {}", ob))) diff --git a/crates/circuit/src/dag_circuit.rs b/crates/circuit/src/dag_circuit.rs index 0b5a43c1eb4b..5edb36d365e0 100644 --- a/crates/circuit/src/dag_circuit.rs +++ b/crates/circuit/src/dag_circuit.rs @@ -2483,16 +2483,8 @@ def _format(operand): true }; let check_conditions = || -> PyResult { - if let Some(cond1) = inst1 - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - { - if let Some(cond2) = inst2 - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - { + if let Some(cond1) = inst1.extra_attrs.condition() { + if let Some(cond2) = inst2.extra_attrs.condition() { legacy_condition_eq .call1((cond1, cond2, &self_bit_indices, &other_bit_indices))? .extract::() @@ -2500,11 +2492,7 @@ def _format(operand): Ok(false) } } else { - Ok(inst2 - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - .is_none()) + Ok(inst2.extra_attrs.condition().is_none()) } }; @@ -3065,11 +3053,7 @@ def _format(operand): let node_map = if propagate_condition && !node.op.control_flow() { // Nested until https://github.com/rust-lang/rust/issues/53667 is fixed in a stable // release - if let Some(condition) = node - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - { + if let Some(condition) = node.extra_attrs.condition() { let mut in_dag = input_dag.copy_empty_like(py, "alike")?; // The remapping of `condition` below is still using the old code that assumes a 2-tuple. // This is because this remapping code only makes sense in the case of non-control-flow @@ -3162,12 +3146,7 @@ def _format(operand): for in_node_index in input_dag.topological_op_nodes()? { let in_node = &input_dag.dag[in_node_index]; if let NodeType::Operation(inst) = in_node { - if inst - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - .is_some() - { + if inst.extra_attrs.condition().is_some() { return Err(DAGCircuitError::new_err( "cannot propagate a condition to an element that already has one", )); @@ -3187,16 +3166,9 @@ def _format(operand): } let mut new_inst = inst.clone(); if new_condition.is_truthy()? { - if let Some(ref mut attrs) = new_inst.extra_attrs { - attrs.condition = Some(new_condition.as_any().clone().unbind()); - } else { - new_inst.extra_attrs = Some(Box::new(ExtraInstructionAttributes { - condition: Some(new_condition.as_any().clone().unbind()), - label: None, - duration: None, - unit: None, - })); - } + new_inst + .extra_attrs + .set_condition(Some(new_condition.as_any().clone().unbind())); #[cfg(feature = "cache_pygates")] { new_inst.py_op.take(); @@ -3281,13 +3253,7 @@ def _format(operand): let raw_target = old_op.instruction.getattr(py, "target")?; let target = raw_target.bind(py); let kwargs = PyDict::new_bound(py); - kwargs.set_item( - "label", - old_inst - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.label.as_ref()), - )?; + kwargs.set_item("label", old_inst.extra_attrs.label())?; let new_op = imports::SWITCH_CASE_OP.get_bound(py).call( ( variable_mapper.map_target(target)?, @@ -3316,11 +3282,7 @@ def _format(operand): } } } - if let Some(condition) = old_inst - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()) - { + if let Some(condition) = old_inst.extra_attrs.condition() { if old_inst.op.name() != "switch_case" { let new_condition: Option = variable_mapper .map_condition(condition.bind(py), false)? @@ -3330,18 +3292,7 @@ def _format(operand): if let NodeType::Operation(ref mut new_inst) = &mut self.dag[*new_node_index] { - match &mut new_inst.extra_attrs { - Some(attrs) => attrs.condition.clone_from(&new_condition), - None => { - new_inst.extra_attrs = - Some(Box::new(ExtraInstructionAttributes { - label: None, - condition: new_condition.clone(), - unit: None, - duration: None, - })) - } - } + new_inst.extra_attrs.set_condition(new_condition.clone()); #[cfg(feature = "cache_pygates")] { new_inst.py_op.take(); @@ -3438,14 +3389,8 @@ def _format(operand): .map(|x| Wire::Clbit(*x)), ) .collect(); - let (additional_clbits, additional_vars) = self.additional_wires( - py, - new_op.operation.view(), - new_op - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref()), - )?; + let (additional_clbits, additional_vars) = + self.additional_wires(py, new_op.operation.view(), new_op.extra_attrs.condition())?; new_wires.extend(additional_clbits.iter().map(|x| Wire::Clbit(*x))); new_wires.extend(additional_vars.iter().map(|x| Wire::Var(x.clone_ref(py)))); @@ -3468,12 +3413,7 @@ def _format(operand): && !(node.instruction.operation.control_flow() || new_op.operation.control_flow()) { // if new_op has a condition, the condition can't be propagated from the old node - if new_op - .extra_attrs - .as_ref() - .and_then(|extra| extra.condition.as_ref()) - .is_some() - { + if new_op.extra_attrs.condition().is_some() { return Err(DAGCircuitError::new_err( "Cannot propagate a condition to an operation that already has one.", )); @@ -3484,17 +3424,8 @@ def _format(operand): "Cannot add a condition on a generic Operation.", )); } - if let Some(ref mut extra) = extra_attrs { - extra.condition = Some(old_condition.clone_ref(py)); - } else { - extra_attrs = ExtraInstructionAttributes::new( - None, - None, - None, - Some(old_condition.clone_ref(py)), - ) - .map(Box::new) - } + extra_attrs.set_condition(Some(old_condition.clone_ref(py))); + let binding = self .control_flow_module .condition_resources(old_condition.bind(py))?; @@ -4991,11 +4922,9 @@ impl DAGCircuit { let filter_fn = move |node_index: NodeIndex| -> Result { let node = &self.dag[node_index]; match node { - NodeType::Operation(inst) => Ok(namelist.contains(inst.op.name()) - && match &inst.extra_attrs { - None => true, - Some(attrs) => attrs.condition.is_none(), - }), + NodeType::Operation(inst) => { + Ok(namelist.contains(inst.op.name()) && inst.extra_attrs.condition().is_none()) + } _ => Ok(false), } }; @@ -6238,7 +6167,7 @@ impl DAGCircuit { clbits: old_node.clbits, params: (!new_gate.1.is_empty()) .then(|| Box::new(new_gate.1.iter().map(|x| Param::Float(*x)).collect())), - extra_attrs: None, + extra_attrs: ExtraInstructionAttributes::default(), #[cfg(feature = "cache_pygates")] py_op: OnceCell::new(), } diff --git a/crates/circuit/src/dag_node.rs b/crates/circuit/src/dag_node.rs index ccae7a8c5d82..4d82cf31e813 100644 --- a/crates/circuit/src/dag_node.rs +++ b/crates/circuit/src/dag_node.rs @@ -368,34 +368,28 @@ impl DAGOpNode { #[getter] fn label(&self) -> Option<&str> { - self.instruction - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.label.as_deref()) + self.instruction.extra_attrs.label() } #[getter] fn condition(&self, py: Python) -> Option { self.instruction .extra_attrs - .as_ref() - .and_then(|attrs| attrs.condition.as_ref().map(|x| x.clone_ref(py))) + .condition() + .map(|x| x.clone_ref(py)) } #[getter] fn duration(&self, py: Python) -> Option { self.instruction .extra_attrs - .as_ref() - .and_then(|attrs| attrs.duration.as_ref().map(|x| x.clone_ref(py))) + .duration() + .map(|x| x.clone_ref(py)) } #[getter] fn unit(&self) -> Option<&str> { - self.instruction - .extra_attrs - .as_ref() - .and_then(|attrs| attrs.unit.as_deref()) + self.instruction.extra_attrs.unit() } /// Is the :class:`.Operation` contained in this node a Qiskit standard gate? @@ -426,30 +420,7 @@ impl DAGOpNode { #[setter] fn set_label(&mut self, val: Option) { - match self.instruction.extra_attrs.as_mut() { - Some(attrs) => attrs.label = val, - None => { - if val.is_some() { - self.instruction.extra_attrs = Some(Box::new( - crate::circuit_instruction::ExtraInstructionAttributes { - label: val, - duration: None, - unit: None, - condition: None, - }, - )) - } - } - }; - if let Some(attrs) = &self.instruction.extra_attrs { - if attrs.label.is_none() - && attrs.duration.is_none() - && attrs.unit.is_none() - && attrs.condition.is_none() - { - self.instruction.extra_attrs = None; - } - } + self.instruction.extra_attrs.set_label(val); } #[getter] diff --git a/crates/circuit/src/operations.rs b/crates/circuit/src/operations.rs index a3fd2fc83c3d..5e7c1818c911 100644 --- a/crates/circuit/src/operations.rs +++ b/crates/circuit/src/operations.rs @@ -425,29 +425,25 @@ impl StandardGate { &self, py: Python, params: Option<&[Param]>, - extra_attrs: Option<&ExtraInstructionAttributes>, + extra_attrs: &ExtraInstructionAttributes, ) -> PyResult> { let gate_class = get_std_gate_class(py, *self)?; let args = match params.unwrap_or(&[]) { &[] => PyTuple::empty_bound(py), params => PyTuple::new_bound(py, params), }; - if let Some(extra) = extra_attrs { - let kwargs = [ - ("label", extra.label.to_object(py)), - ("unit", extra.py_unit(py).into_any()), - ("duration", extra.duration.to_object(py)), - ] - .into_py_dict_bound(py); - let mut out = gate_class.call_bound(py, args, Some(&kwargs))?; - if let Some(ref condition) = extra.condition { - out = out.call_method0(py, "to_mutable")?; - out.setattr(py, "condition", condition)?; - } - Ok(out) - } else { - gate_class.call_bound(py, args, None) + let kwargs = [ + ("label", extra_attrs.label().to_object(py)), + ("unit", extra_attrs.py_unit(py).into_any()), + ("duration", extra_attrs.duration().to_object(py)), + ] + .into_py_dict_bound(py); + let mut out = gate_class.call_bound(py, args, Some(&kwargs))?; + if let Some(condition) = extra_attrs.condition() { + out = out.call_method0(py, "to_mutable")?; + out.setattr(py, "condition", condition)?; } + Ok(out) } pub fn num_ctrl_qubits(&self) -> u32 { diff --git a/crates/circuit/src/packed_instruction.rs b/crates/circuit/src/packed_instruction.rs index df8f9801314a..82c678031a15 100644 --- a/crates/circuit/src/packed_instruction.rs +++ b/crates/circuit/src/packed_instruction.rs @@ -497,7 +497,7 @@ pub struct PackedInstruction { /// The index under which the interner has stored `clbits`. pub clbits: Interned<[Clbit]>, pub params: Option>>, - pub extra_attrs: Option>, + pub extra_attrs: ExtraInstructionAttributes, #[cfg(feature = "cache_pygates")] /// This is hidden in a `OnceCell` because it's just an on-demand cache; we don't create this @@ -548,16 +548,12 @@ impl PackedInstruction { #[inline] pub fn condition(&self) -> Option<&Py> { - self.extra_attrs - .as_ref() - .and_then(|extra| extra.condition.as_ref()) + self.extra_attrs.condition() } #[inline] pub fn label(&self) -> Option<&str> { - self.extra_attrs - .as_ref() - .and_then(|extra| extra.label.as_deref()) + self.extra_attrs.label() } /// Build a reference to the Python-space operation object (the `Gate`, etc) packed into this @@ -573,7 +569,7 @@ impl PackedInstruction { OperationRef::Standard(standard) => standard.create_py_op( py, self.params.as_deref().map(SmallVec::as_slice), - self.extra_attrs.as_deref(), + &self.extra_attrs, ), OperationRef::Gate(gate) => Ok(gate.gate.clone_ref(py)), OperationRef::Instruction(instruction) => Ok(instruction.instruction.clone_ref(py)),