Add DAGCircuit.get_var() (and get_stretch()) for parity with QuantumCircuit and CircuitData
What should we add?
Summary
DAGCircuit exposes has_var(name_or_var) but has no corresponding get_var(name) to retrieve an expr.Var by name. This is inconsistent with both QuantumCircuit and CircuitData, and makes name-based variable lookup awkward when working directly with DAGs (e.g. during transpiler passes or substitute_node_with_dag).
Current behavior
On DAGCircuit you can:
- Check presence:
dag.has_var("foo")/dag.has_var(var_obj) - Iterate:
dag.iter_input_vars(),dag.iter_declared_vars(), etc. - Count:
dag.num_vars,dag.num_declared_vars, etc.
But there is no:
dag.get_var("foo") # AttributeErrorBy contrast:
QuantumCircuithasget_var(name, default=...)and documents it next tohas_var(qiskit/circuit/quantumcircuit.py).CircuitDataalready exposesget_var(name)via the Rust bindings (crates/circuit/src/circuit_data.rs).The shared Rust container
VarStretchContaineralready implements name lookup:pub fn get_var(&self, name: &str) -> Option<&expr::Var> pub fn get_stretch(&self, name: &str) -> Option<&expr::Stretch>and
DAGCircuitalready exposes this internally viavars_stretches_view()— it just isn't wired to Python.
Expected behavior
Add Python methods on DAGCircuit, mirroring CircuitData / QuantumCircuit:
dag.get_var(name: str) -> expr.Var | None
dag.get_stretch(name: str) -> expr.Stretch | NoneOptionally also get_identifier(name) for symmetry with has_identifier.
Suggested semantics (matching CircuitData):
- Return the
expr.Var/expr.Stretchfor the given name, orNoneif not found. - Alternatively, support a
defaultkwarg likeQuantumCircuit.get_varfor API consistency across the stack.
Motivation / use case
When manipulating DAGs with realtime variables (control flow, classical expressions, substitute_node_with_dag, etc.), it is common to need the actual expr.Var object for a known name — for example:
- Building a
wiresdict for variable remapping during substitution - Detecting name collisions vs UUID mismatches (where
has_var("foo")isTruebut the replacement DAG carries a differentexpr.Varwith the same name) - Writing transpiler passes that mirror logic already written against
QuantumCircuit
Today the workaround is to scan iterators:
var = next((v for v in dag.iter_declared_vars() if v.name == "foo"), None)which is verbose and easy to get wrong (must check the right iterator for input/capture/declare scope).
Proposed implementation
Likely a small PyO3 addition in crates/circuit/src/dag_circuit.rs, delegating to the existing VarStretchContainer::get_var / get_stretch, similar to CircuitData::py_get_var:
#[pyo3(name = "get_var")]
fn py_get_var(&self, py: Python, name: &str) -> PyResult<Py<PyAny>> {
if let Some(var) = self.inner.vars_stretches.get_var(name) {
var.clone().into_py_any(py)
} else {
Ok(py.None())
}
}Acceptance criteria
-
DAGCircuit.get_var(name)returns the correctexpr.Varfor input, capture, and declared variables - Returns
None(or raisesKeyErrorwithout default — pick one and document) when absent -
get_stretch(name)added for parity (optional but recommended) - Tests added alongside existing
has_vartests - Docstring references
has_varas the companion check method (mirroringQuantumCircuit)
Source: Qiskit/qiskit