Source code for qarp.resources._vector

"""The resource-vector contract: frozen, versioned, wire-stable.

``ResourceVector`` is the schema external consumers (resource-estimation
frameworks, surrogate training) code against.  ``to_dict()`` is the wire format; it is
pinned by a golden test and must never change silently — additive changes
bump ``SCHEMA_VERSION``.

Two semantic rules the schema encodes:

* ``None`` is not ``0``.  ``swap_count=None`` means "routing has not
  happened", never "no SWAPs"; ``t_count=None`` means "not expressible as a
  count at this stage", never "zero T gates".
* Counted and modeled quantities never mix.  ``t_count`` only ever comes
  from gates present in the circuit; ``t_count_modeled`` / ``extras`` are
  filled exclusively by a :class:`~qarp.resources.ResourceModeler`
  and carry a ``provenance.modeler`` tag saying which one.

This module may import only stdlib — everything in ``qarp.resources``
depends on it.
"""

from dataclasses import asdict, dataclass, field, replace
from enum import Enum
from typing import Any

# Wire-format version consumers pin against: every change is additive and
# bumps this number, which never moves without a migration.
SCHEMA_VERSION = 1


[docs] class Stage(str, Enum): """Compilation stage a :class:`ResourceVector` snapshot describes.""" LOGICAL = "logical" # as-authored (block.flatten()), no transpilation OPTIMIZED = "optimized" # rebased + peephole-optimized to the gate set ROUTED = "routed" # after routing, PRE-rebase: the only stage with SWAPs TARGET = "target" # routed then rebased back to the target gate set SYNTHESIZED = "synthesized" # Rz → Clifford+T at declared ε (approximate)
[docs] @dataclass(frozen=True) class Provenance: """What produced the numbers — makes every vector self-describing.""" stage: Stage gateset: str | None = None # e.g. "clifford_t", "clifford_t_rz" opt_level: str | None = None # "O0" | "O1" | "O2" router: str | None = None # "Sabre" | "Lite"; ROUTED/TARGET only device: str | None = None # user-supplied device label modeler: str | None = None # e.g. "my_modeler:eps=1e-10" synthesis: str | None = None # e.g. "gridsynth:eps=1e-10"; SYNTHESIZED only
[docs] @dataclass(frozen=True) class ResourceVector: """One stage snapshot of a circuit's resources. ``n_qubits`` is the logical width before routing and the physical device width at ROUTED/TARGET. ``n_gates`` counts unitary operations only (barriers, global phases, branch markers, measurements and resets are excluded). The arity buckets partition it exactly: ``n_1q + n_2q + n_3q_plus == n_gates`` at every stage. ``swap_count`` is router-inserted overhead, defined only at ROUTED (user-authored SWAPs appear in ``op_histogram`` and ``n_2q`` at every stage). """ n_qubits: int depth: int n_gates: int n_1q: int n_2q: int n_3q_plus: int # gates on 3+ qubits (CCX, CSWAP, wide MCZ); completes the partition n_measurements: int n_resets: int t_count: int | None # counted T+Tdg; None while any parametric/opaque unitary remains swap_count: int | None # None unless stage is ROUTED op_histogram: dict[str, int] # count_ops() minus pseudo-ops (Barrier/GPhase/markers) provenance: Provenance t_count_modeled: float | None = None extras: dict[str, float] = field(default_factory=dict)
[docs] def to_dict(self) -> dict[str, Any]: """Wire format (schema pinned by test_vector.py's golden test).""" d = asdict(self) d["provenance"]["stage"] = self.provenance.stage.value d["schema_version"] = SCHEMA_VERSION return d
[docs] @classmethod def from_dict(cls, d: dict[str, Any]) -> "ResourceVector": d = dict(d) version = d.pop("schema_version") if version != SCHEMA_VERSION: raise ValueError( f"resource vector schema_version {version} != supported {SCHEMA_VERSION}" ) prov = dict(d.pop("provenance")) prov["stage"] = Stage(prov["stage"]) return cls(provenance=Provenance(**prov), **d)
[docs] def with_model( self, *, modeler: str, t_count_modeled: float | None, extras: dict[str, float], ) -> "ResourceVector": """Copy with modeler-filled fields set (the only sanctioned mutation).""" return replace( self, t_count_modeled=t_count_modeled, extras=extras, provenance=replace(self.provenance, modeler=modeler), )