#17019·qiskit

Add DAGCircuit.get_var() (and get_stretch()) for parity with QuantumCircuit and CircuitData

Author: arthurostraussCreated Sep 20, 2026Updated Sep 20, 2026
Labelstype: feature request

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:

python
dag.get_var("foo")  # AttributeError

By contrast:

  • QuantumCircuit has get_var(name, default=...) and documents it next to has_var (qiskit/circuit/quantumcircuit.py).

  • CircuitData already exposes get_var(name) via the Rust bindings (crates/circuit/src/circuit_data.rs).

  • The shared Rust container VarStretchContainer already implements name lookup:

    rust
    pub fn get_var(&self, name: &str) -> Option<&expr::Var>
    pub fn get_stretch(&self, name: &str) -> Option<&expr::Stretch>

    and DAGCircuit already exposes this internally via vars_stretches_view() — it just isn't wired to Python.

Expected behavior

Add Python methods on DAGCircuit, mirroring CircuitData / QuantumCircuit:

python
dag.get_var(name: str) -> expr.Var | None
dag.get_stretch(name: str) -> expr.Stretch | None

Optionally also get_identifier(name) for symmetry with has_identifier.

Suggested semantics (matching CircuitData):

  • Return the expr.Var / expr.Stretch for the given name, or None if not found.
  • Alternatively, support a default kwarg like QuantumCircuit.get_var for 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 wires dict for variable remapping during substitution
  • Detecting name collisions vs UUID mismatches (where has_var("foo") is True but the replacement DAG carries a different expr.Var with the same name)
  • Writing transpiler passes that mirror logic already written against QuantumCircuit

Today the workaround is to scan iterators:

python
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:

rust
#[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 correct expr.Var for input, capture, and declared variables
  • Returns None (or raises KeyError without default — pick one and document) when absent
  • get_stretch(name) added for parity (optional but recommended)
  • Tests added alongside existing has_var tests
  • Docstring references has_var as the companion check method (mirroring QuantumCircuit)