quchip.sweep

Declarative parameter sweeps for chip studies.

This module provides the user-facing sweep abstractions and a dressed-spectrum sweep driver:

  • Sweep declares a 1-D axis of values for a single named parameter. Multiple Sweep axes compose as a Cartesian product.

  • ZippedSweep (built with Sweep.zip()) pairs axes element-wise rather than expanding the product, which is the right shape for correlated scans (e.g. frequency and drive amplitude moved together along a calibration trace).

  • SpectrumSweep walks a sweep grid, dresses the chip at each point via Chip.dress(), and records eigenvalues and bare→dressed label assignments into a SpectrumSweepResult.

Sweep values are stored in their native physical units (GHz for frequencies, ns for times, mK for temperatures). Sweep preserves JAX arrays verbatim so downstream code can differentiate through swept values when desired; everything else is promoted to np.ndarray so len and indexing behave predictably.

The dressed-spectrum machinery here computes eigenvalues and overlaps of the lab-frame chip Hamiltonian; for a general reference on the dressed-state picture of coupled circuit qubits, see Blais, Grimsmo, Girvin & Wallraff, Circuit quantum electrodynamics, Rev. Mod. Phys. 93, 025005 (2021).

Classes

SpectrumSweep(chip, axes, *, update_fn[, ...])

Sequential dressed-spectrum sweep driver.

SpectrumSweepResult(device_labels, ...[, ...])

Dressed-spectrum data collected across a SpectrumSweep grid.

Sweep(values, *[, name])

Declarative 1-D sweep axis over a named parameter.

ZippedSweep(sweeps)

Element-wise pairing of sweep axes.

class quchip.sweep.ZippedSweep(sweeps)[source]

Bases: object

Element-wise pairing of sweep axes.

Built via Sweep.zip(). All bundled axes must share the same size; iteration steps through them together, producing one parameter dict per element rather than a Cartesian-product grid.

Parameters:

sweeps (tuple[Sweep, ...])

class quchip.sweep.Sweep(values, *, name=None)[source]

Bases: object

Declarative 1-D sweep axis over a named parameter.

values are in the swept parameter’s own physical units (the package-wide contract: GHz for frequencies and couplings, ns for times, mK for temperatures).

values may be a Python sequence, a NumPy array, or a JAX array; JAX arrays are preserved as-is so any consumer that threads the sweep through a JAX-traced path keeps full differentiability. Non-JAX inputs are normalized through numpy.asarray() for uniform len/indexing behavior.

Examples

>>> import numpy as np
>>> from quchip.sweep import Sweep
>>> freqs = Sweep(np.linspace(4.9, 5.1, 5), name="freq")
>>> freqs.size
5
>>> drives = Sweep([0.01, 0.02, 0.03], name="amp")
>>> points = Sweep.expand([freqs, drives])
>>> len(points)
15
Parameters:
  • values (Any)

  • name (str | None)

static zip(*sweeps)[source]

Pair axes for element-wise iteration.

At least two sweeps are required; all sweeps must have equal size. A ValueError is raised otherwise.

Parameters:

*sweeps (Sweep) – Two or more Sweep axes to bundle element-wise. Their name attributes must all be distinct — a ZippedSweep with repeated axis names raises ValueError when it is later enumerated (expand(), SpectrumSweep).

Returns:

Bundle iterated element-wise instead of taken in Cartesian product with other axes.

Return type:

ZippedSweep

static expand(axes)[source]

Expand sweep axes into a flat list of parameter dicts.

The Cartesian product of independent Sweep axes is taken; ZippedSweep bundles remain element-wise. The return order is the grid’s C-order (last axis varies fastest).

This is the params-only view of _iter_axis_points() — the single enumeration code path — with the grid coordinates dropped.

Parameters:

axes (Sequence[Sweep | ZippedSweep]) – Sweep and/or ZippedSweep axes. Every axis name (including each member of a zipped bundle) must be unique across axes, else ValueError is raised — a repeated name would silently overwrite itself in each point’s parameter dict.

Returns:

One parameter dict per grid point, length equal to the product of the independent axes’ sizes (zipped bundles contribute their shared size once).

Return type:

list[dict[str, Any]]

class quchip.sweep.SpectrumSweepResult(device_labels, bare_labels, eigenvalues, dressed_indices, assignment_overlaps, overlap_threshold, params, eigenvector_matrices=None, eigenstates=None)[source]

Bases: object

Dressed-spectrum data collected across a SpectrumSweep grid.

Parameters:
device_labels

Device labels of the underlying chip, ordered as the chip’s tensor-product order (devices).

Type:

tuple[str, …]

bare_labels

Tuple of bare-state labels (one tuple of occupation numbers per device) that the sweep tracks.

Type:

tuple[tuple[int, …], …]

eigenvalues

(*grid_shape, n_evals) array of eigenvalues in GHz, one row per sweep point.

Type:

numpy.ndarray

dressed_indices

(*grid_shape, n_bare) array giving the dressed-state index assigned to each bare label at each sweep point, or NaN when no confident assignment is found.

Type:

numpy.ndarray

assignment_overlaps

(*grid_shape, n_bare) overlap weights accompanying dressed_indices.

Type:

numpy.ndarray

overlap_threshold

Minimum overlap below which an assignment is treated as unreliable and returned as NaN.

Type:

float

params

Object array of parameter dicts, one per grid point.

Type:

numpy.ndarray

eigenvector_matrices

Optional object array of dressed eigenvector matrices (one per grid point), produced when store_eigenstates=True.

Type:

numpy.ndarray | None

eigenstates

Optional object array of dressed eigenstates aligned with eigenvector_matrices.

Type:

numpy.ndarray | None

device_labels: tuple[str, ...]
bare_labels: tuple[tuple[int, ...], ...]
eigenvalues: ndarray
dressed_indices: ndarray
assignment_overlaps: ndarray
overlap_threshold: float
params: ndarray
eigenvector_matrices: ndarray | None = None
eigenstates: ndarray | None = None
property shape: tuple[int, ...]

Sweep-grid shape (excludes the trailing eigenvalue axis).

dressed_index(device_states=None, /, **device_state_kwargs)[source]

Dressed-state indices across the sweep for one bare label.

Entries where the assignment overlap falls below overlap_threshold (or was never found) are returned as NaN.

Parameters:
  • device_states (Mapping[str, int] | None) – Mapping from device (label or object) to occupation number, resolved via bare_label_from_mapping(); devices left unspecified default to Fock index 0. Mutually exclusive with device_state_kwargs.

  • **device_state_kwargs (int) – Keyword form of device_states (device label as keyword).

Returns:

Floating-point array of shape shape (the sweep grid, excluding the bare-label axis). Floating dtype is required to represent unreliable or missing assignments as NaN — a grid point whose overlap with this bare label falls below overlap_threshold, or where no dressed state was assigned to it at all.

Return type:

numpy.ndarray

Examples

>>> # Track the bare |1, 0> state across a sweep
>>> # result.dressed_index({qubit_a: 1, qubit_b: 0})
energy_by_bare_label(device_states=None, /, **device_state_kwargs)[source]

Dressed energies (GHz) traced out for one bare label across the sweep.

Invalid points inherit the NaN mask from dressed_index().

Parameters:
  • device_states (Mapping[str, int] | None) – Mapping from device (label or object) to occupation number; see dressed_index(). Mutually exclusive with device_state_kwargs.

  • **device_state_kwargs (int) – Keyword form of device_states (device label as keyword).

Returns:

Floating-point array of shape shape, in GHz. Entries where dressed_index() returns NaN (overlap below overlap_threshold, or no assignment found) are NaN.

Return type:

numpy.ndarray

state_components_at(point, state=None, /, *, n_components=5, **device_state_kwargs)[source]

Top-n_components bare-state probabilities of a dressed eigenstate.

state may either be an explicit dressed index (int) or a bare-label specification that is resolved via the same overlap-based map used by dressed_index(). Requires the sweep to have been run with store_eigenstates=True.

Returns a dict mapping bare labels to squared-amplitude probabilities, ordered from largest to smallest.

Parameters:
Return type:

dict[tuple[int, …], float]

class quchip.sweep.SpectrumSweep(chip, axes, *, update_fn, evals_count=None, store_eigenstates=False, overlap_threshold=0.5)[source]

Bases: object

Sequential dressed-spectrum sweep driver.

At each grid point the chip is cloned via Chip.updated(), update_fn mutates the clone with the swept values, and Chip.dress() computes the lab-frame dressed spectrum. The per-point eigenvalues and overlap-based bare→dressed assignments are collected into a SpectrumSweepResult.

This is the standard tool for two-tone spectroscopy maps, avoided crossings, and any study that requires following dressed states across parameter space. See Blais, Grimsmo, Girvin & Wallraff, Rev. Mod. Phys. 93, 025005 (2021), for the dressed-state picture; the overlap-based labeling follows the usual practice of assigning a dressed eigenstate to the bare state with which it has maximum overlap.

Examples

>>> # Sweep a qubit frequency and record dressed levels
>>> # sweep = SpectrumSweep(
>>> #     chip,
>>> #     [Sweep(np.linspace(4.8, 5.2, 41), name="freq")],
>>> #     update_fn=lambda c, p: setattr(c.device(qubit), "frequency", p["freq"]),
>>> # )
>>> # result = sweep.run()
Parameters:
run(progress=True)[source]

Execute the sweep and collect the dressed-spectrum analysis.

Parameters:

progress (bool) – Whether to display a tqdm progress bar.

Returns:

Populated result.

Return type:

SpectrumSweepResult