microsoft / microsoft/qdk-ec

qodec: Unify Reference and model-path resolution

Open
#180 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
64
Forks
16
Avg merge
1d 5h
Merged PRs (30d)
5

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the existing Reference, ReferenceLike, resolve, Node, and Gadget entry points, then trace how Rust implementations are exposed through the Python bindings. Define and test the shared resolution behavior, including relative and root-relative addresses, slices and unions, standalone gadget ownership, and missing targets, while preserving serialized references and parity meanings.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.