Source code for quchip.inverse_design.observables

"""Target-spec construction for ``fit_a_dress``.

The fit takes two independent target channels from the user
(``coupling_targets``, ``observable_targets``) and merges them with
each device's *current* ``freq``/``anharmonicity`` — which act as
default anchors — into a single flat tuple of :class:`TargetSpec`
records. Explicit observable targets always override same-``(kind,
label)`` device defaults; couplings contribute a target only when
listed in ``coupling_targets``.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from quchip.utils.labeling import resolve_label


[docs] @dataclass(frozen=True) class TargetSpec: """A single optimization target. Attributes ---------- kind Observable kind — one of ``"freq"``, ``"anharmonicity"``, ``"chi"``, ``"zz"``, ``"exchange"``, ``"g"``. label Device label, ``(label_a, label_b)`` tuple, or coupling label that locates this target on the chip. target Desired value in GHz. """ kind: str label: Any target: float
[docs] def infer_coupling_mode(coupling, override: str | None = None) -> str: """Infer whether a coupling's strength is targeted as ``"g"``, ``"chi"``, or ``"zz"``. When both devices are computational, the user almost always cares about the static ``zz``; a qubit-resonator pair is instead best anchored by the dispersive ``chi``; everything else is fit through the bare coupling strength. An explicit ``override`` short circuits the heuristic — including to ``"chi"`` on a pair that would not otherwise infer it; :func:`build_target_specs` validates that a ``"chi"`` target always has exactly one computational endpoint, whichever path selected it. Parameters ---------- coupling The coupling whose mode is being inferred. override Explicit mode (``"g"``, ``"chi"``, or ``"zz"``), or ``None`` to use the computational-endpoint heuristic. Returns ------- str ``"g"``, ``"chi"``, or ``"zz"``. """ if override is not None: return override a_comp = getattr(coupling.device_a, "computational", False) b_comp = getattr(coupling.device_b, "computational", False) if a_comp and b_comp: return "zz" if a_comp or b_comp: return "chi" return "g"
def _validate_chi_target(chip, spec: TargetSpec) -> None: """Ensure a ``"chi"`` :class:`TargetSpec` resolves to a coupling with exactly one computational endpoint. Applied to every ``"chi"`` spec regardless of origin — an auto device-implied target, a ``coupling_targets`` entry, or an explicit ``observable_targets`` entry — since ``_chi`` (the observable this spec anchors) is only physically meaningful between one qubit (computational) endpoint and one readout (non-computational) endpoint. """ coupling = next((c for c in chip.couplings if c.label == spec.label), None) if coupling is None: raise ValueError( f"'chi' target label {spec.label!r} does not match any coupling on the chip; " "'chi' (dispersive shift) targets must be keyed by a coupling label." ) a_comp = getattr(coupling.device_a, "computational", False) b_comp = getattr(coupling.device_b, "computational", False) if a_comp == b_comp: raise ValueError( f"Coupling {coupling.label!r} is targeted as 'chi', which requires exactly one " "computational endpoint (chi is the dispersive shift of a readout mode " f"conditioned on a qubit state); got device_a={coupling.device_a_label!r} " f"computational={a_comp}, device_b={coupling.device_b_label!r} computational={b_comp}." ) def _coerce_explicit_target_specs(observable_targets: dict | None) -> tuple[TargetSpec, ...]: """Normalize ``observable_targets`` into a tuple of :class:`TargetSpec`.""" if not observable_targets: return () specs: list[TargetSpec] = [] for key, metrics in observable_targets.items(): if not isinstance(metrics, dict): raise TypeError( "observable_targets values must be dicts mapping observable kinds to numeric targets, " f"got {type(metrics).__name__}" ) resolved_key = ( tuple(resolve_label(part) for part in key) if isinstance(key, tuple) else resolve_label(key) ) for kind, target in metrics.items(): specs.append(TargetSpec(str(kind), resolved_key, float(target))) return tuple(specs)
[docs] def build_target_specs(chip, coupling_targets: dict, observable_targets: dict | None = None) -> tuple[TargetSpec, ...]: """Merge device defaults, coupling targets, and explicit observables. The returned ordering is: 1. For every device, the *dressed* 0→1 transition frequency (``chip.freq(device)``) is anchored. Computational devices also anchor on the dressed anharmonicity (``chip.dressed_anharmonicity(device)``). Anchoring the dressed observable rather than the bare device attribute makes the defaults model-agnostic — Duffing, charge-basis transmon, and fluxonium all expose a dressed 0→1 spacing, even when the bare parametrization is wholly different (no ``freq`` or ``anharmonicity`` attribute). 2. One coupling target per entry in ``coupling_targets``, with mode resolved via :func:`infer_coupling_mode` (override if a string mode is supplied, heuristic otherwise). 3. Every explicit entry from ``observable_targets``. An explicit ``(kind, label)`` in ``observable_targets`` suppresses the same-``(kind, label)`` default from steps 1 and 2, so the user never ends up with duplicate anchors for the same quantity. Returns ------- tuple[TargetSpec, ...] Merged specs in the order documented above. Raises ------ ValueError A coupling target mode is not one of ``"chi"``, ``"zz"``, ``"g"``; or any ``"chi"`` target in the merged result — from an auto device-implied default, a ``coupling_targets`` entry, or an explicit ``observable_targets`` entry — does not resolve to a coupling with exactly one computational endpoint (see :func:`_validate_chi_target`). """ explicit_specs = _coerce_explicit_target_specs(observable_targets) explicit_keys = {(spec.kind, spec.label) for spec in explicit_specs} normalized_coupling_targets = {resolve_label(key): value for key, value in coupling_targets.items()} specs: list[TargetSpec] = [] for device in chip.devices: spec = TargetSpec("freq", device.label, float(chip.freq(device))) if (spec.kind, spec.label) not in explicit_keys: specs.append(spec) if device.computational and device.levels >= 3: spec = TargetSpec( "anharmonicity", device.label, float(chip.dressed_anharmonicity(device)), ) if (spec.kind, spec.label) not in explicit_keys: specs.append(spec) for coupling in chip.couplings: if coupling.label not in normalized_coupling_targets: continue mode = infer_coupling_mode(coupling, normalized_coupling_targets[coupling.label]) if mode not in ("chi", "zz", "g"): raise ValueError(f"Unsupported coupling target mode {mode!r} for {coupling.label!r}") spec = TargetSpec(mode, coupling.label, float(coupling.coupling_strength)) if (spec.kind, spec.label) not in explicit_keys: specs.append(spec) specs.extend(explicit_specs) for spec in specs: if spec.kind == "chi": _validate_chi_target(chip, spec) return tuple(specs)