Skip to content

qodec: Unify Reference and model-path resolution #180

Description

Is your feature request related to a problem? Please describe.

qodec has two ways to address the same declaration. A gadget equation uses
in[0].stabilizers[1], while model navigation requires
inputs[0].code.stabilizers[1]. Callers must translate between them.

For a loaded protocol with a measure_z gadget, current code looks like:

from qodec.gadgets import Encoding, Reference

reference = Reference("in[0].stabilizers[1]")
node = protocol.resolve('layers[0].gadgets["measure_z"]')
encoding = node.resolve("inputs[0]").value(Encoding)
operator = encoding.code.stabilizers[1]

Reference exposes the parsed parts, but cannot be passed directly to the
lookup. This is an API convenience gap, not a need for new protocol semantics.

Describe the solution you'd like

Use one Reference type for addresses throughout a qodec. Let resolve on a
protocol, node, or gadget accept either Reference or str through the existing
ReferenceLike alias. Strings use the same grammar; a Reference reuses its
parsed form. For a single target, return the existing Node type.

Proposed usage, not supported by the current API:

gadget_node = protocol.resolve('layers[0].gadgets["measure_z"]')
operator_node = gadget_node.resolve("in[0].stabilizers[1]")
operator = operator_node.value(str)

assert gadget_node.resolve(reference) == operator_node

The root-relative form should also work:

operator = protocol.resolve(
		'layers[0].gadgets["measure_z"].in[0].stabilizers[1]'
).value(str)

And a gadget obtained directly should offer the same operation:

from qodec import Gadget

gadget = gadget_node.value(Gadget)
operator = gadget.resolve(reference).value(str)

This broadens Python/Rust addressing, not which references are legal in gadget
equations. Reference('metadata["description"]') could be resolved as a model
address but must remain invalid as a parity term. Keep serialized references
and parity meanings unchanged.

Lookup returns a declaration, not a sampled bit or an evaluated equation. The
operator above still uses code-local indices; its encoding support supplies
the circuit block labels. Resolution does not compute global placement.

Describe alternatives you've considered

  • Document the manual translation: possible today, but leaves callers doing
    the same work repeatedly.
  • Add resolve_operator: useful only for encoding operators, not other model
    values. General resolve fits the existing navigation API better.
  • Add a second reference type: preserves the split instead of removing it.

Reuse Reference, resolve, and Node. The new method is Gadget.resolve;
existing protocol and node methods gain Reference input support. No new
result type is proposed. Shared behavior belongs in Rust, with thin bindings.

Additional context

A few choices still need settling:

  • Treat in[0].stabilizers[1] as an alias for the model path, or choose a
    consistent spelling. Do not silently rewrite authored references on save.
  • Define results for slices and unions, preserving order and duplicates.
  • Keep circuit parsing explicit: circuit.readouts[i] needs interpretation,
    whereas ordinary model lookup must work with unsupported circuit source.
  • Define node ownership and paths for standalone gadgets, while retaining
    existing protocol-node identity, mutation behavior, and optional source locations.
  • Preserve string-based lookup and reference equality/hashing. Missing targets
    should raise rather than produce partial results.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions