quchip.utils.labeling

Shared auto-labeling and label resolution for quchip components.

quchip devices, couplings, and drives never require users to invent unique identifiers. Each component class declares a short _type_prefix (e.g. "duffing" for DuffingTransmon, "charge" for ChargeDrive, "capacitive" for Capacitive), and when the user constructs one without passing an explicit label the component calls auto_label() to obtain "{prefix}_{n}" — where n is a per-prefix counter that increments on each call.

Labels are strings that uniquely identify a component inside a Chip. They are used as:

  • Keys in QuantumSequence drive schedules.

  • Keys in Crosstalk matrices.

  • Keys in expectation-value dictionaries.

  • The user-facing identifier in plots, __repr__, and error messages.

Any public API that accepts a component reference accepts either the string label or the component object itself. resolve_label() is the single helper that normalizes both into a string — call it wherever you’d otherwise write a manual isinstance(..., str) branch.

Examples

>>> from quchip.utils.labeling import auto_label, reset_label_counters, resolve_label
>>> reset_label_counters()
>>> auto_label("duffing")
'duffing_0'
>>> auto_label("duffing")
'duffing_1'
>>> auto_label("capacitive")
'capacitive_0'
>>> class FakeDevice:
...     label = "duffing_0"
>>> resolve_label(FakeDevice())
'duffing_0'
>>> resolve_label("duffing_0")
'duffing_0'

Functions

auto_label(prefix)

Return the next "{prefix}_{n}" label and advance the per-prefix counter.

bare_label_from_mapping(device_labels, ...)

Merge a {device: Fock} spec into a full bare-state tuple.

merge_labeled_values(mapping, kwargs)

Resolve a {device-or-label: value} mapping + kwargs into one {label: value} dict.

reset_label_counters()

Reset every auto-label counter to zero.

resolve_label(obj)

Return a label string from either a string or a labeled component.

top_components(eigenvector_matrix, ...)

Return the leading bare-basis probabilities |⟨bare|dressed⟩|² of one dressed column.

Classes

LabelKeyedDict

Result mapping keyed by labels (or label tuples) that also accepts the objects themselves.

quchip.utils.labeling.auto_label(prefix)[source]

Return the next "{prefix}_{n}" label and advance the per-prefix counter.

Parameters:

prefix (str) – The component’s _type_prefix (e.g. "duffing", "charge").

Returns:

f"{prefix}_{n}" where n starts at 0 and increments on each call with the same prefix.

Return type:

str

quchip.utils.labeling.reset_label_counters()[source]

Reset every auto-label counter to zero.

Intended for test fixtures — tests rely on deterministic labels, and the module-level counter dict is otherwise process-global.

Return type:

None

quchip.utils.labeling.resolve_label(obj)[source]

Return a label string from either a string or a labeled component.

Used wherever quchip’s public API accepts “component or its label” interchangeably.

Parameters:

obj (str | Any) – Either a label string, or any object exposing a non-None .label attribute (devices, drives, couplings all qualify).

Raises:

TypeError – If obj is neither a string nor an object with a usable .label.

Return type:

str

quchip.utils.labeling.merge_labeled_values(mapping, kwargs)[source]

Resolve a {device-or-label: value} mapping + kwargs into one {label: value} dict.

Keys in mapping are resolved through resolve_label(), so device objects and label strings are interchangeable. Two mapping keys that resolve to the same label, or a label supplied through both mapping and kwargs, are duplicates and raise ValueError. Values are placed verbatim (no int cast) — callers that need type or bound validation layer it on top.

The single resolve-and-dedup step shared by the bare-tuple builder (bare_label_from_mapping()) and the chip state-factory normalizer (quchip.chip.states.normalize_device_state_mapping()), so the rule for what counts as a duplicate device specification can never drift between spectroscopy sweeps and single-point state construction. mapping must be a Mapping (or None); the public state factory type-checks user input before delegating here.

Parameters:
Return type:

dict[str, Any]

quchip.utils.labeling.bare_label_from_mapping(device_labels, mapping, kwargs)[source]

Merge a {device: Fock} spec into a full bare-state tuple.

The single canonical definition of how a partial {device: Fock} specification becomes a complete bare-state tuple in device_labels order: keys are resolved and de-duplicated via merge_labeled_values(), any label outside device_labels is unknown and raises, and devices not mentioned default to Fock index 0.

Both SpectrumSweepResult and the chip-side dressed analysis (quchip.chip.analysis) route through here, so the spec-to-tuple mapping lives in exactly one place. Values are placed verbatim (no int cast): callers that need type or Fock-bound validation layer it on top of the returned tuple.

Parameters:
Return type:

tuple[int, …]

class quchip.utils.labeling.LabelKeyedDict[source]

Bases: dict

Result mapping keyed by labels (or label tuples) that also accepts the objects themselves.

Wherever a reduction or analysis result is keyed by a component’s label, the component object is an equally good key. Tuple keys (survivor pairs) additionally match in either order — a pair is unordered physics; the stored order is bookkeeping. Iteration, items(), and serialization see the plain stored keys; only lookup is widened.

get(key, default=None)[source]

Return the value for key, or default if absent (via __getitem__()).

Parameters:
Return type:

Any

quchip.utils.labeling.top_components(eigenvector_matrix, bare_labels, dressed_idx, n)[source]

Return the leading bare-basis probabilities |⟨bare|dressed⟩|² of one dressed column.

Squares the amplitudes of column dressed_idx of eigenvector_matrix (dressed eigenvectors expressed in the bare product basis), sorts them descending, and maps the top n to their bare labels. This is the squared-amplitude / argsort / label-map kernel shared by quchip.chip.analysis.ChipAnalysis.state_components() and quchip.sweep.SpectrumSweepResult.state_components_at(); each call site keeps only its own dressed-index resolution.

Operates on a concrete eigenvector matrix — both call sites resolve the dressed index off an already-materialized (non-traced) dressing, so the NumPy reduction here never sits on a differentiable path.

Parameters:
Return type:

dict[tuple[int, …], float]