Source code for quchip.inverse_design.subsystems
"""Neighborhood extraction for large-chip observables.
When the full Hilbert space is too large to dress repeatedly inside a
least-squares loop, ``fit_a_dress`` evaluates each target on the
smallest sub-chip that still contains the relevant physics: the target
device(s) plus every directly coupled neighbor (one-hop closure).
This is a pragmatic truncation, not a controlled approximation — the
one-hop neighborhood captures all first-order dispersive effects for
the target but ignores second-order contributions from non-neighbors.
Use ``max_hilbert_dim`` in :func:`~quchip.inverse_design.fit.fit_a_dress`
to control when the fit switches from ``"full"`` to ``"local"``.
"""
from __future__ import annotations
from typing import Any
from quchip.chip import Chip
from quchip.utils.labeling import resolve_label
[docs]
def choose_evaluator(chip: Chip, max_hilbert_dim: int) -> str:
"""Pick ``"full"`` or ``"local"`` based on total Hilbert-space size.
Returns ``"full"`` when the product of all device ``levels`` is at
most ``max_hilbert_dim``; otherwise ``"local"``.
Parameters
----------
chip : Chip
max_hilbert_dim : int
Total-Hilbert-space-size threshold.
Returns
-------
str
``"full"`` or ``"local"``.
"""
return "full" if chip.total_dim <= max_hilbert_dim else "local"
[docs]
def build_local_subsystem(chip: Chip, labels: tuple[str, ...]) -> Chip:
"""Build a reduced ``Chip`` holding only the given device labels.
Couplings are retained iff both endpoint labels are in ``labels``;
frame, RWA, and backend settings are inherited from the parent.
Parameters
----------
chip : Chip
labels : tuple[str, ...]
Device labels to keep.
Returns
-------
Chip
Reduced chip holding only the kept devices and their mutual
couplings.
"""
keep = set(labels)
devices = [device for device in chip.devices if device.label in keep]
couplings = [
coupling
for coupling in chip.couplings
if coupling.device_a_label in keep and coupling.device_b_label in keep
]
return Chip(devices=devices, couplings=couplings, frame=chip.frame, rwa=chip.rwa, backend=chip.backend)
[docs]
def device_labels_for_local_eval(chip: Chip, label: Any) -> tuple[str, ...]:
"""Return the seed device(s) plus every directly coupled neighbor.
``label`` may be a single device/label or a tuple of device/labels;
all entries are normalized through
:func:`~quchip.utils.labeling.resolve_label`. The returned tuple is
sorted for determinism.
Parameters
----------
chip : Chip
label : Any
A single device/label or a tuple of devices/labels.
Returns
-------
tuple[str, ...]
Sorted device labels: the seed(s) plus every directly coupled
neighbor.
Examples
--------
A q0 - q1 - q2 chain keeps the seed and its direct neighbors:
>>> from quchip import Chip, DuffingTransmon, Capacitive
>>> from quchip.inverse_design.subsystems import device_labels_for_local_eval
>>> qs = [DuffingTransmon(freq=5.0 + 0.1 * i, anharmonicity=-0.3, levels=3,
... label=f"q{i}") for i in range(3)]
>>> chip = Chip(devices=qs, couplings=[Capacitive(qs[0], qs[1], g=0.005),
... Capacitive(qs[1], qs[2], g=0.005)])
>>> device_labels_for_local_eval(chip, "q0")
('q0', 'q1')
>>> device_labels_for_local_eval(chip, ("q0", "q1"))
('q0', 'q1', 'q2')
"""
seeds = {resolve_label(part) for part in label} if isinstance(label, tuple) else {resolve_label(label)}
labels = set(seeds)
for coupling in chip.couplings:
if coupling.device_a_label in seeds:
labels.add(coupling.device_b_label)
if coupling.device_b_label in seeds:
labels.add(coupling.device_a_label)
return tuple(sorted(labels))