quchip.inverse_design

Inverse design from a numerical dressed-chip specification.

Component classes declare which input numbers are dressed targets and which bare parameters normally move. fit_a_dress() compiles those declarations without evaluating the desired chip, then solves the resulting static observable problem with SciPy and an exact JAX Jacobian when available. The fitted chip remains traceable and differentiable downstream.

quchip.inverse_design.fit_a_dress(chip, *, constraints=None, vary=None, start=None, coupling_targets=None, observable_targets=None, fit_parameters=None, max_hilbert_dim=10000, seed_strength_bounds=(1e-06, 0.25), max_nfev=1000)[source]

Find bare parameters for a numerically specified dressed chip.

fit_a_dress(desired) reads component-owned target declarations without evaluating desired. Common spectral devices interpret their declared frequencies and anharmonicities as dressed targets. A Capacitive edge between two non-computational modes targets the dressed exchange_rate. Other Capacitive edges and CrossKerr couplings target the full cross_kerr = E11 - E10 - E01 + E00. The returned chip is a fitted clone; desired is never mutated.

constraints adds or replaces numerical observables, vary replaces the component-owned free-parameter selection, and start replaces selected starting values. The deprecated coupling_targets, observable_targets, and fit_parameters keywords keep their seed-chip semantics through 0.2.x. They cannot be mixed with the desired-chip keywords and will be removed in 0.3.0.

Parameters:
  • chip (Chip) – Desired dressed-chip specification. Component declarations supply numerical targets; no dressed analysis is run on this object.

  • constraints (Mapping | None) – Additional {component_or_pair: {observable: value_or_none}} constraints. Supported canonical observables are "freq", "anharmonicity", "cross_kerr", "exchange_rate", and "coupling_strength". "zz"/"static_zz", "exchange", and "g" are accepted aliases. An explicit value replaces the same component default; None removes it. Device pairs need not be direct coupling edges.

  • vary (Mapping | None) – Complete desired-chip allowlist of bare parameters that may move: {component_or_label: name_collection}. When omitted, each component’s conservative inverse-design policy is used. Components absent from an explicit mapping are frozen.

  • start (Mapping | None) – Optional {"<component>.<parameter>": value} replacements for the selected optimizer starting values. A key not selected by vary raises instead of being silently ignored.

  • coupling_targets (Mapping | None) – Deprecated since 0.2.1; use constraints. Maps a coupling (or its label) to a target mode: "chi", "zz", or "g". For listed couplings, the coupling’s current strength is interpreted as the target value for that mode. With fit_parameters=None, couplings not listed here are still free — they are optimized, just without a dedicated anchor; a fit_parameters mapping can freeze them regardless (a coupling target does not itself make a coupling free). A "chi" target requires the coupling to have exactly one computational endpoint; both-computational or neither-computational raises ValueError at construction.

  • observable_targets (Mapping | None) – Deprecated since 0.2.1; put numerical dressed values on chip and add extra observables with constraints. Keys are devices/labels or (device_a, device_b) tuples; values are {kind: value} dicts. Supported kinds: "freq", "anharmonicity" (device), "exchange", "zz" (pair). Device-level targets override the auto-targeted defaults for the same (kind, label).

  • fit_parameters (Mapping | None) – Deprecated since 0.2.1; use vary. None in the compatibility path selects every declared device tunable (tunable_params()) and every coupling’s scalar strength is free. A mapping is the complete free-parameter allowlist: {component_or_label: name_collection}, where a device’s name_collection is a subset of its declared tunable names and a coupling’s is a subset of (coupling.coupling_strength_name,). A component (device or coupling, given as the object or its label) absent from the mapping is fully frozen — it does not default to free. An empty name_collection explicitly freezes a listed component. A bare string value (e.g. "E_J" instead of ("E_J",)) is rejected, since a string is itself a collection of characters. Selected parameters are packed in chip order and each component’s own declared parameter order, not mapping or tuple order. initial_params / final_params contain only the selected (free) parameters.

  • max_hilbert_dim (int) – Above this total Hilbert-space size the fit switches from dressing the whole chip to dressing one-hop subsystems per target (see quchip.inverse_design.subsystems).

  • seed_strength_bounds (tuple[float, float]) – (lo, hi) magnitude bounds for the bare-coupling-strength seed root solve (_estimate_bare_g()) used for cross_kerr/chi/zz coupling targets. The target observable must be bracketed by the values at these two endpoints, or seeding raises ValueError rather than silently returning a saturated endpoint.

  • max_nfev (int) – Maximum number of residual evaluations for the SciPy Trust-Region Reflective solver.

Returns:

Fitted chip clone, loss, residual history, per-target ObservableReport tuples, packed parameters, and solver metadata.

Return type:

FitADressResult

Raises:

ValueError – Desired-chip and deprecated compatibility keyword families are mixed; a "chi" coupling target does not have exactly one computational endpoint, or a chi/zz seed’s target observable is not bracketed within seed_strength_bounds; an automatic desired-chip plan is underdetermined by count or rank; a fit_parameters key does not resolve to a device or coupling label on chip, names a parameter the resolved component does not declare, resolves the same label twice, or is a bare string rather than a name collection; or fit_parameters selects zero free parameters overall.

Warns:

UserWarning – A fit using explicit vary or deprecated compatibility keywords is underdetermined by target count or final scaled-Jacobian rank. Explicit plans are returned with diagnostics because the caller has taken ownership of the ambiguity; automatic desired-chip plans raise instead.

Notes

Residuals are normalized by max(|target|, 1e-9) so every anchor contributes on equal relative-error footing. A coupling’s scalar strength bounds are symmetric around zero — the sign of a capacitive-type coupling is physical and must not be constrained. The solver’s convergence tolerances (ftol/xtol/gtol = 1e-11) and its x_scale floor (1e-3, applied per parameter as max(abs(x0), 1e-3)) are fixed fitter policy, not exposed as options.

Identifiability. The free-parameter-vs-residual count is necessary but not sufficient. The fitter therefore computes the final SVD rank and condition number from normalized residuals in the same scaled parameter coordinates used by the solver. A custom DeviceModel whose tunable_param_names is discovered (the derived default, not an explicit declaration) is not automatically fit-ready: an unbounded parameter still needs a tunable_param_bounds() rule before the optimizer can search it.

JAX traceability. When the chip uses a JAX-native backend, the complete parameter-to-residual map and its exact Jacobian are JAX-traceable; SciPy receives their concrete values for bounded trust-region control. The optimizer itself is not differentiated. Other backends retain SciPy’s numerical Jacobian. The returned chip remains fully traceable and differentiable in either case.

class quchip.inverse_design.FitADressResult(chip, loss, history, initial_targets, final_targets, initial_params, final_params, solver_info, parameter_reports=())[source]

Bases: object

Result of a fit_a_dress() optimization run.

Parameters:
chip

Fitted chip: a clone of the desired specification (or compatibility seed) with updated device and coupling parameters. The input chip is never mutated. Exposing .chip makes this satisfy ChipTransform structurally, with no inheritance required.

Type:

Chip

loss

Final objective (sum of squared, scale-normalized residuals).

Type:

float

history

One-dimensional numpy array containing the normalized objective at every distinct parameter vector passed to the residual function. The first entry is the seed and the last is loss. With a numerical Jacobian, the intermediate entries include finite-difference probes as well as accepted solver iterates; use numpy.minimum.accumulate(history) for a monotone best-so-far convergence curve.

Type:

Any

initial_targets

One ObservableReport per target, evaluated on the optimizer’s initial candidate.

Type:

tuple[ObservableReport, …]

final_targets

One ObservableReport per target, evaluated on the fitted chip.

Type:

tuple[ObservableReport, …]

initial_params

{parameter_name: seed_value} — the starting point passed to the optimizer.

Type:

dict[str, float]

final_params

{parameter_name: fitted_value} — the optimizer output. Parameter names follow "<device>.freq", "<device>.anharmonicity", and "<coupling>.<coupling_strength_name>""<coupling>.g" for Capacitive, "<coupling>.g_0" for TunableCapacitive, "<coupling>.chi" for CrossKerr.

Type:

dict[str, float]

parameter_reports

One FitParameterReport per varied bare parameter, including its bounds, starting-point source, and any coupling-sign choice.

Type:

tuple[FitParameterReport, …]

solver_info

scipy solver metadata (method, status, message, nfev, jacobian), plus the identifiability receipt recorded for every fit_a_dress() call: n_free_parameters (length of final_params), n_target_residuals (length of final_targets), and underdetermined_by_count (True when the former exceeds the latter — a necessary, not sufficient, identifiability condition), final scaled-Jacobian rank, condition number, singular values, and any weak parameter directions. Rank uses normalized residuals in the solver’s scaled parameter coordinates. history_axis names the sampling axis used by history, and n_recorded_evaluations gives its length. jacobian is "jax" when a JAX-native backend supplies the exact residual Jacobian and "finite-difference" otherwise.

Type:

dict[str, Any]

chip: Chip
loss: float
history: Any
initial_targets: tuple[ObservableReport, ...]
final_targets: tuple[ObservableReport, ...]
initial_params: dict[str, float]
final_params: dict[str, float]
solver_info: dict[str, Any]
parameter_reports: tuple[FitParameterReport, ...] = ()
summary()[source]

Return a compact target, parameter, and identifiability receipt.

Return type:

str

rebind(seed: BaseDevice | str, /) BaseDevice[source]
rebind(seed: BaseDevice | str, /, *more: BaseDevice | str) tuple[BaseDevice, ...]

Look up the fitted clones matching one or more seed devices.

Use fit.rebind(qb, tc, cr) to retrieve the fitted clones corresponding to the seed devices.

Parameters:

*seeds (BaseDevice or str) – One or more devices (or their labels) from the seed chip passed to fit_a_dress(). At least one is required.

Returns:

The matching device(s) on chip (the fitted clone), in input order. A single positional seed returns that device directly; two or more return a tuple.

Return type:

BaseDevice or tuple[BaseDevice, …]

Raises:

ValueError – No seeds were given.

class quchip.inverse_design.FitParameterReport(name, initial, final, lower_bound, upper_bound, seed_source, sign_choice=None)[source]

Bases: object

Starting point, bounds, result, and provenance for one bare parameter.

Parameters:
name: str
initial: float
final: float
lower_bound: float
upper_bound: float
seed_source: str
sign_choice: str | None = None
property delta: float

Signed optimizer displacement, final - initial (GHz).

class quchip.inverse_design.ObservableReport(kind, label, target, initial, final, evaluator, source='legacy')[source]

Bases: object

Per-target record from a fit_a_dress run.

Parameters:
kind

Canonical desired-chip kind ("freq", "anharmonicity", "cross_kerr", "exchange_rate", or "coupling_strength"), or its deprecated compatibility counterpart.

Type:

str

label

Target locator — a device label for single-device observables, a (label_a, label_b) tuple for pair observables, or a coupling label for coupling-keyed observables.

Type:

Any

target

The value the optimizer tried to match (GHz).

Type:

float

initial

Observable value at the seed chip, before optimization (GHz).

Type:

float

final

Observable value at the fitted chip, after optimization (GHz).

Type:

float

evaluator

"full" if this target was evaluated on the whole chip or "local" if it was evaluated on a one-hop subsystem (see max_hilbert_dim in fit_a_dress()).

Type:

str

source

"component default" or "explicit" for the desired-chip contract; "legacy" for the deprecated compatibility path.

Type:

str

kind: str
label: Any
target: float
initial: float
final: float
evaluator: str
source: str = 'legacy'
property residual: float

Final signed error, final - target (GHz).

property relative_residual: float

Final residual on the fitter’s normalized objective scale.

Modules

fit

Fit bare chip parameters to a numerical dressed specification.

observables

Target compilation for quchip.fit_a_dress().

subsystems

Neighborhood extraction for large-chip observables.

types