quchip

Public package surface for quchip.

Visualization helpers and optional third-party interop are loaded lazily through the module-level __getattr__() so that import quchip stays fast and does not force any optional dependency on the core install.

Functions

enable_compilation_cache([path, ...])

Enable JAX's on-disk persistent compilation cache (opt-in).

class quchip.Approximation[source]

Bases: ABC

Immutable engine strategy applied after authored physics is assembled.

filters_terms: bool = False
keeps_operator_band(weights)[source]

Return whether a structural operator band survives reduction.

Parameters:

weights (tuple[int, ...])

Return type:

bool

to_dict()[source]

Return the stable serialized strategy tag.

Return type:

dict[str, Any]

static from_dict(data)[source]

Restore one explicit strategy and reject retired schemas.

Parameters:

data (Any)

Return type:

Approximation

class quchip.Exact[source]

Bases: Approximation

Retain every term in the authored finite-dimensional Hamiltonian.

class quchip.RWA(keep_bands=None)[source]

Bases: Approximation

First-order structural rotating-wave selection.

keep_bands replaces, rather than extends, total-excitation conservation.

Parameters:

keep_bands (frozenset[tuple[int, ...]] | None)

keep_bands: frozenset[tuple[int, ...]] | None
filters_terms: bool = True
keeps_operator_band(weights)[source]

Return whether a structural operator band survives reduction.

Parameters:

weights (tuple[int, ...])

Return type:

bool

class quchip.CollapseChannel(operator, rate, name)[source]

Bases: object

One unscaled Lindblad operator and its rate in inverse nanoseconds.

Parameters:
operator: Any
rate: Any
name: str
class quchip.CouplingModel(device_a, device_b, *, label=None, **params)[source]

Bases: BaseCoupling

Declarative two-body coupling base.

Subclasses declare physics parameters via parameter() and implement interaction() (returning a PhysicsExpr over the two endpoint operators). The chip’s approximation strategy is applied structurally by the engine after the authored interaction is assembled. Optional overrides:

  • time_terms() — time-dependent Hamiltonian terms, each pairing a local operator with a public time coefficient.

coupling_strength defaults to the first declared parameter field (suited for the common case of one g-like scalar). Override the property in subclasses with a different convention.

Note

Coupling instances are not registered as JAX pytrees and cannot be passed as dynamic jax.jit / jax.vmap / jax.grad arguments. Coupling parameters remain differentiable when the coupling (and the devices or chip it couples) is constructed from traced arguments inside the transformed function.

Examples

>>> from quchip.declarative import CouplingModel, parameter, Scalar
>>> class ExchangeCoupling(CouplingModel):
...     g: Scalar = parameter(unit="GHz")
...     def interaction(self, a, b, p):
...         return p.g * (a.a * b.adag + a.adag * b.a)
>>> c = ExchangeCoupling("q0", "q1", g=0.01)
>>> c.coupling_strength
0.01
Parameters:
device_a: BaseDevice | str
device_b: BaseDevice | str
label: Any
property coupling_strength: Any

Primary scalar coupling strength, defaulting to the first parameter.

property coupling_strength_name: str

Display name of coupling_strength, defaulting to the first parameter field.

interaction(a, b, p)[source]

Return the full two-body interaction expression.

Parameters:
  • a (EndpointOps) – Operator namespaces for the two coupled endpoints. Same-endpoint operators compose with @; cross-endpoint operators combine with * (tensor product).

  • b (EndpointOps) – Operator namespaces for the two coupled endpoints. Same-endpoint operators compose with @; cross-endpoint operators combine with * (tensor product).

  • p (Any)

Returns:

The interaction Hamiltonian expression, in ordinary-frequency units (GHz).

Return type:

PhysicsExpr

time_terms(a, b, p)[source]

Return time-dependent interaction terms.

Parameters:
  • a (EndpointOps) – Operator namespaces for the two coupled endpoints.

  • b (EndpointOps) – Operator namespaces for the two coupled endpoints.

  • p (Any)

Returns:

Local operators and their scalar time coefficients. The empty tuple denotes a purely static coupling.

Return type:

tuple of TimeDependentTerm

parametric_interaction(a, b, p)[source]

Return the parametric interaction structure, or None when this coupling is not modulable.

The coupling-side mirror of the device drive-dispatch protocols: a ParametricDrive accepts any coupling whose hook returns a PhysicsExpr.

Parameters:
Return type:

Any

dissipation(a, b, p)[source]

Return authored two-endpoint Lindblad channels.

Parameters:
Return type:

tuple[CollapseChannel, …]

interaction_hamiltonian()[source]

Return the authored symbolic interaction Hamiltonian.

Return type:

Any

collapse_channels()[source]

Return normalized coupling collapse channels.

Return type:

tuple[CollapseChannel, …]

classmethod from_dict(d, device_a, device_b)[source]

Reconstruct a coupling from to_dict() output.

Default implementation: forward declared parameters straight into __init__. Subclasses with bespoke serialization (e.g. envelope modulations) override this.

Parameters:
Return type:

CouplingModel

to_dict()[source]

Serialize common coupling state plus declared parameter values.

Return type:

dict[str, Any]

class quchip.CosineCoefficient(amplitude=unbound, frequency=unbound, phase=0.0)[source]

Bases: TimeCoefficient

Cosine coefficient with amplitude in GHz and frequency in GHz.

Parameters:
  • amplitude (Any)

  • frequency (Any)

  • phase (Any)

amplitude: Any = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol=None, noise=False, kw_only=False, required=False)
frequency: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol=None, noise=False, kw_only=False, required=False)
phase: Any = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit='rad', symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Evaluate the coefficient at time t in ns.

Parameters:

t (Any)

Return type:

Any

class quchip.DeviceModel(*, levels=2, label=None, **params)[source]

Bases: BaseDevice

Declarative base for physics device models.

Subclasses declare their parameters as annotated class attributes using parameter() (e.g. freq: Scalar = parameter(positive=True)) and implement local_hamiltonian(). The declared parameters become positional-or-keyword __init__ arguments and JAX pytree leaves so the full instance is traceable / differentiable / sweepable end-to-end.

The hamiltonian() adapter compiles the declarative expression returned by local_hamiltonian() into an operator for the active default backend.

Examples

>>> from quchip.declarative import DeviceModel, parameter, Scalar
>>> class DuffingOscillator(DeviceModel):
...     freq: Scalar = parameter(positive=True, unit="GHz")
...     anharmonicity: Scalar = parameter(unit="GHz")
...     def local_hamiltonian(self, op, p):
...         return p.freq * op.n + 0.5 * p.anharmonicity * op.n @ (op.n - op.I)
>>> device = DuffingOscillator(freq=5.0, anharmonicity=-0.3, levels=4)
>>> device.freq
5.0
Parameters:
  • levels (int)

  • label (Any)

  • params (Any)

levels: int
label: Any
T1: Any = Parameter(default=None, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=True, kw_only=True, required=False)
T2: Any = Parameter(default=None, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=True, kw_only=True, required=False)
thermal_population: Any = Parameter(default=None, positive=False, nonnegative=True, serialize=True, unit=None, symbol=None, noise=True, kw_only=True, required=False)
approximation: ClassVar[str | None] = None

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

computational: ClassVar[bool] = False

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

structural_setting_names: ClassVar[tuple[str, ...]] = ()
validate()[source]

Cross-field validation hook, run at the end of construction.

Default is a no-op. Subclasses override to enforce constraints that span multiple declared parameters (e.g. 2 * edge <= duration). Checks must be gated on concrete scalars via quchip.utils.jax_utils.maybe_concrete_scalar() so traced parameters never force concretization.

Return type:

None

local_hamiltonian(op, p)[source]

Return this device’s local Hamiltonian as a declarative expression.

Parameters:
  • op (LocalOps) – Operator namespace for this device’s endpoint, exposing a, adag, n, I and the Pauli handles as composable PhysicsExpr nodes.

  • p (ParameterNamespace) – Symbolic leaves for the parameters declared on this model.

Returns:

The local Hamiltonian expression, in ordinary-frequency units (GHz).

Return type:

PhysicsExpr

time_terms(op, p)[source]

Return local time-dependent Hamiltonian terms beyond the static model.

Parameters:
Return type:

tuple[TimeDependentTerm, …]

dissipation(op, p)[source]

Return device-local Lindblad channels.

The base channels implement T1, T2, and thermal occupation. Subclasses may append channels with super().dissipation(op, p).

Parameters:
Return type:

tuple[CollapseChannel, …]

unresolved_hamiltonian()[source]

Return the authored symbolic local Hamiltonian.

Return type:

Any

to_dict()[source]

Serialize common device state plus declared parameter values.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct the device from to_dict() output.

Parameters:

d (dict[str, Any])

Return type:

DeviceModel

physics_notes()[source]

Return base device notes plus the declared approximation, if any.

Return type:

list[str]

quchip.EndpointOps

alias of LocalOps

class quchip.Envelope(**params)[source]

Bases: Registrable, ABC

Local complex pulse shape evaluated relative to its scheduled start.

Parameters:

params (Any)

duration: Any
validate()[source]

Validate relations between concrete parameters.

Return type:

None

abstractmethod value(local_time)[source]

Return complex I/Q shape at time relative to the pulse start.

Parameters:

local_time (Any)

Return type:

Any

sample(local_time, *, real=False)[source]

Evaluate the shape on an array, optionally returning only I.

Parameters:
Return type:

Any

to_dict()[source]

Serialize the concrete type and its declared parameters.

Return type:

dict[str, Any]

class quchip.LocalOps(label, space, device=None)[source]

Bases: object

Declarative operator namespace for one local Hilbert space endpoint.

Passed as op to DeviceModel.local_hamiltonian() and as the two endpoints a, b to CouplingModel.interaction(). Each property returns a PhysicsExpr that composes with +, -, @ (same endpoint), * (scalar or tensor product).

Examples

>>> from quchip.declarative import LocalOps
>>> from quchip.devices.spaces import FockSpace
>>> op = LocalOps(label="q", space=FockSpace(3))
>>> H = 5.0 * op.n + 0.5 * (op.adag @ op.adag @ op.a @ op.a)
>>> H.kind
'add'
Parameters:
label: str
space: LocalSpace
device: Any = None
property a: PhysicsExpr

Lowering operator for this endpoint.

property adag: PhysicsExpr

Raising operator for this endpoint.

property n: PhysicsExpr

Number operator for this endpoint.

property level: PhysicsExpr

Energy-level index operator in the authored local basis.

property n2: PhysicsExpr

Squared charge operator in a compatible authored local space.

property phi: PhysicsExpr

Phase operator in a compatible authored local space.

property cos_phi: PhysicsExpr

Cosine of phase in a compatible authored local space.

property sin_phi: PhysicsExpr

Sine of phase in a compatible authored local space.

property I: PhysicsExpr

Identity operator for this endpoint.

property x: PhysicsExpr

Unnormalized quadrature x = a + a† (no 1/sqrt(2) factor).

property charge: PhysicsExpr

Physical charge-like drive operator for this local representation.

property sigma_x: PhysicsExpr

|0><1| + |1><0| on the computational |0>, |1> subspace of the truncated space.

property sigma_y: PhysicsExpr

-i|0><1| + i|1><0| on the computational |0>, |1> subspace.

property sigma_z: PhysicsExpr

|0><0| - |1><1| on the computational |0>, |1> subspace.

property sigma_plus: PhysicsExpr

Raising operator |1><0| on the computational subspace.

property sigma_minus: PhysicsExpr

Lowering operator |0><1| on the computational subspace.

class quchip.TimeDependentTerm(operator, coefficient)[source]

Bases: object

An authored local operator and its time coefficient.

Parameters:
operator: Any
coefficient: TimeCoefficient
class quchip.Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)[source]

Bases: object

Metadata for a declarative model parameter field.

The metadata is intentionally lightweight: it records validation and serialization intent while leaving the runtime value fully traceable. Sign constraints (positive / nonnegative) are enforced only on concrete scalars, so traced values flow through unchecked.

Parameters:
default: Any = unbound
positive: bool = False
nonnegative: bool = False
serialize: bool = True
unit: str | None = None
symbol: str | None = None
noise: bool = False
kw_only: bool = False
required: bool = False
class quchip.PhysicsExpr(kind, args=(), labels=(), _bindings=<factory>)[source]

Bases: object

Authored scalar and operator algebra, independent of numerical values.

Parameters:
kind: str
args: tuple[Any, ...] = ()
labels: tuple[str, ...] = ()
classmethod parameter(*, scope, name, symbol=None, unit=None)[source]

Create a symbolic declared-parameter leaf.

Parameters:
  • scope (str)

  • name (str)

  • symbol (str | None)

  • unit (str | None)

Return type:

PhysicsExpr

classmethod literal(value)[source]

Create a literal scalar leaf.

Parameters:

value (Any)

Return type:

PhysicsExpr

classmethod from_matrix(value, *, labels, dims, name=None)[source]

Create a named backend-neutral matrix contribution.

Parameters:
Return type:

PhysicsExpr

classmethod from_function(function, *arguments, labels, dims, name=None)[source]

Create an opaque matrix-valued contribution from a pure function.

The function runs only during numerical materialization. Display keeps its declared name and arguments, such as X(a, b), without exposing the implementation as symbolic algebra.

Parameters:
Return type:

PhysicsExpr

classmethod from_state(value, *, labels, dims, name)[source]

Create a named authored ket contribution.

Parameters:
Return type:

PhysicsExpr

classmethod from_state_function(function, *arguments, labels, dims, name)[source]

Create an opaque callable ket contribution.

Parameters:
Return type:

PhysicsExpr

classmethod from_signal(signal, *, name='f')[source]

Create a scalar time-function leaf backed by an engine signal.

Parameters:
Return type:

PhysicsExpr

embed(labels, dims)[source]

Embed this local contribution into an ordered composite Hilbert space.

Parameters:
Return type:

PhysicsExpr

with_bindings(bindings)[source]

Attach default values used only by direct numerical inspection.

Parameters:

bindings (Mapping[str, Any])

Return type:

PhysicsExpr

parameter_paths()[source]

Return referenced dotted parameter paths in authored order.

Return type:

tuple[str, …]

numeric_values()[source]

Return bound and matrix payloads for tracer-safe cache decisions.

Return type:

tuple[Any, …]

property shape: tuple[int, int]

Matrix shape implied by this operator expression’s static support.

latex()[source]

Render the authored expression with familiar mathematical notation.

Return type:

str

matrix(bindings=None, *, t=None, backend=None)[source]

Materialize this expression and return its dense numerical array.

Parameters:
Return type:

Any

quchip.Scalar

alias of Any

class quchip.Setting(default=unbound, serialize=True, kw_only=True)[source]

Bases: object

Metadata for a serialized, non-traceable structural model choice.

Parameters:
default: Any = unbound
serialize: bool = True
kw_only: bool = True
class quchip.TimeCoefficient[source]

Bases: Registrable, ABC

Scalar coefficient of a component’s time-dependent Hamiltonian term.

Time is measured in ns. Implementations use quchip.qnp so values remain JAX-traceable without receiving an array namespace argument.

abstractmethod value(t)[source]

Evaluate the coefficient at time t in ns.

Parameters:

t (Any)

Return type:

Any

to_dict()[source]

Serialize the concrete coefficient and its numerical fields.

Return type:

dict[str, Any]

quchip.as_operator_expr(value, *, labels, dims, name, arguments=(), owner=None, scope=None, allowed=None)[source]

Normalize symbolic, matrix, or opaque callable operator authorship.

Parameters:
Return type:

PhysicsExpr

quchip.as_scalar_expr(value, *, name, arguments=(), owner=None, scope=None, allowed=None)[source]

Normalize symbolic, numeric, or opaque callable scalar authorship.

Parameters:
Return type:

PhysicsExpr

quchip.as_state_expr(value, *, labels, dims, name, arguments=(), owner=None, scope=None, allowed=None)[source]

Normalize an authored ket array or opaque callable without evaluating it.

Parameters:
Return type:

PhysicsExpr

quchip.parameter(*, default=<object object>, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False)[source]

Declare a traceable numerical parameter on a model class.

unit is display metadata for human-readable surfaces such as Chip.describe() — the package-wide units contract (GHz, ns, mK) still governs the value itself. None means dimensionless or unknown. Returns a Parameter field descriptor that parameter_fields() collects at class-definition time.

Parameters:
  • default (Any, optional) – Declared default value. When omitted the parameter remains unbound until numerical materialization.

  • positive (bool, optional) – Reject concrete values <= 0. Traced values pass unchecked.

  • nonnegative (bool, optional) – Reject concrete values < 0. Traced values pass unchecked.

  • serialize (bool, optional) – Include the field in to_dict() output.

  • unit (str or None, optional) – Display-only unit label (e.g. "GHz").

  • symbol (str or None, optional) – Mathematical symbol used when displaying authored physics. The field name is used when omitted.

  • noise (bool, optional) – Whether Chip.set_noise() may configure this field while its current value is unset.

  • kw_only (bool)

Return type:

Any

Examples

>>> from quchip.declarative import DeviceModel, parameter, Scalar
>>> class Oscillator(DeviceModel):
...     freq: Scalar = parameter(positive=True, unit="GHz")
...     def local_hamiltonian(self, op, p):
...         return p.freq * op.n
>>> Oscillator(freq=5.0, levels=3).freq
5.0
quchip.setting(*, default=unbound, serialize=True, kw_only=True)[source]

Declare serialized structural configuration on a model class.

Parameters:
Return type:

Any

class quchip.ChargeBasisTransmon(E_C, E_J, n_g=0.0, levels=None, label=None, *, num_basis=61, basis=None, collapse_model='fermi_golden', coupling_channel=None, collapse_rate_threshold=1e-08, **noise)[source]

Bases: DeviceModel

Transmon with its Hamiltonian authored in the integer-charge basis.

Parameters:
  • E_C (Any)

  • E_J (Any)

  • n_g (Any)

  • levels (int)

  • label (Any)

  • num_basis (int)

  • basis (Literal['native', 'eigen'] | None)

  • collapse_model (Literal['fermi_golden', 'ladder'])

  • coupling_channel (Literal['charge'] | None)

  • collapse_rate_threshold (float)

  • noise (Any)

tunable_param_names = ('E_C', 'E_J', 'n_g')

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

approximation = 'Exact diagonalization in a finite integer-charge basis; accuracy is governed by num_basis.'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

computational = True

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

requires_projection_levels: ClassVar[bool] = True
structural_setting_names = ('num_basis', 'basis', 'projection_levels', 'collapse_model', 'coupling_channel', 'collapse_rate_threshold')
E_C: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='E_C', noise=False, kw_only=False, required=False)
E_J: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='E_J', noise=False, kw_only=False, required=False)
n_g: Scalar = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol='n_g', noise=False, kw_only=False, required=False)
dissipation(op, p)[source]

Return device-local Lindblad channels.

The base channels implement T1, T2, and thermal occupation. Subclasses may append channels with super().dissipation(op, p).

Parameters:
Return type:

tuple[CollapseChannel, …]

local_space()[source]

Return the authored integer-charge space.

Return type:

ChargeSpace

local_hamiltonian(op, p)[source]

Return 4 E_C (n - n_g)^2 - E_J cos(phi).

Parameters:
Return type:

PhysicsExpr

property freq: Any

Return the isolated 0-to-1 transition in GHz.

eigenenergies()[source]

Return isolated energies shifted to zero at the ground state.

Return type:

Any

eigenvectors()[source]

Return the isolated energy-ordered eigenvectors in the charge basis.

Return type:

Any

charge_coupling_operator()[source]

Return the authored charge operator.

Return type:

Any

phase_coupling_operator()[source]

Return sin(phi) in the authored charge basis.

Return type:

Any

tunable_param_bounds(name, value)[source]

Return the physical charge period for n_g.

Parameters:
Return type:

tuple[float, float]

physics_notes()[source]

Return base device notes plus the declared approximation, if any.

Return type:

list[str]

classmethod from_frequency(freq, anharmonicity, n_g=0.0, levels=None, label=None, *, num_basis=61, basis=None, **kwargs)[source]

Construct from the leading transmon-regime inversion.

Parameters:
Return type:

ChargeBasisTransmon

to_dict()[source]

Serialize common device state plus declared parameter values.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Reconstruct the device from to_dict() output.

Parameters:

data (dict[str, Any])

Return type:

ChargeBasisTransmon

class quchip.ChargeSpace(num_basis)[source]

Bases: LocalSpace

Finite integer-charge basis centered on zero charge.

Parameters:

num_basis (int)

num_basis: int
property dimension: int

Return the authored local-space dimension.

matrix(name)[source]

Return one named operator as a JAX-compatible dense array.

Parameters:

name (str)

Return type:

Any

class quchip.CustomSpace(dimension, operators)[source]

Bases: LocalSpace

Named local operators supplied as matrices or zero-argument JAX callables.

Parameters:
  • dimension (int)

  • operators (Mapping[str, Any])

property dimension: int

Return the authored local-space dimension.

matrix(name)[source]

Return one named operator as a JAX-compatible dense array.

Parameters:

name (str)

Return type:

Any

class quchip.DuffingTransmon(freq=unbound, anharmonicity=unbound, *, levels=3, label=None, T1=None, T2=None, thermal_population=None)[source]

Bases: FockDevice

Transmon modelled as a weakly anharmonic Duffing oscillator.

Parameters:
  • freq (float) – Bare 0 -> 1 transition frequency ω in GHz. Must be positive. May be a JAX tracer for sweeps / gradients.

  • anharmonicity (float) – Anharmonicity α in GHz. Typically negative for superconducting transmons (e.g. -0.25 GHz). May be a JAX tracer.

  • levels (int, default 3) – Fock-space truncation. Three levels suffice for leakage-aware single-qubit modelling; increase for higher-level physics (e.g. iSWAP-family gates via the |02>-|11> crossing).

  • label (str | None, default None) – If omitted, auto-generated as duffing_{idx} via the shared labeling counter.

  • **noise_kwargs – Forwarded to BaseDeviceT1, T2, thermal_population.

  • T1 (Any)

  • T2 (Any)

  • thermal_population (Any)

Example

>>> from quchip.devices import DuffingTransmon
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, T1=30_000.0, T2=20_000.0)
>>> len(q.collapse_operators()) >= 1
True
tunable_param_names = ('freq', 'anharmonicity')

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

dressed_fit_target_fields = (('freq', 'freq'), ('anharmonicity', 'anharmonicity'))

(dressed_observable, declared_field) pairs used when this device appears in the desired-chip form of fit_a_dress. Empty means that the model makes no automatic dressed-target claim; circuit-level models can remain fixed until the user supplies explicit constraints.

dressed_fit_param_names = ('freq', 'anharmonicity')

Bare parameters normally varied to reproduce dressed_fit_target_fields. This remains separate from tunable_param_names: a model may expose parameters for sweeps without claiming that inverse design can identify all of them from its default dressed observables.

approximation = 'Duffing expansion: cosine Josephson potential truncated at 4th order.'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

computational = True

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

freq: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='\\omega', noise=False, kw_only=False, required=False)
anharmonicity: Scalar = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol='\\alpha', noise=False, kw_only=False, required=False)
local_hamiltonian(op, p)[source]

Return the local Duffing Hamiltonian H = omega n + (alpha/2) n (n - I).

Parameters:
Return type:

PhysicsExpr

physics_notes()[source]

Return declared Duffing-approximation validity notes.

Return type:

list[str]

class quchip.FockDevice(*, levels=2, label=None, **params)[source]

Bases: DeviceModel

Device with explicit conventional oscillator coupling operators.

Subclasses still own their Hamiltonian and approximation. This base only declares the standard Fock-space operators used by charge, phase, and frequency-modulating drives.

Parameters:
  • levels (int)

  • label (Any)

  • params (Any)

abstractmethod local_hamiltonian(op, p)[source]

Declare this device’s local Hamiltonian in its Fock space.

Parameters:
Return type:

PhysicsExpr

charge_coupling_operator()[source]

Return the conventional charge quadrature i(a - a†).

Return type:

PhysicsExpr

phase_coupling_operator()[source]

Return the conventional phase quadrature a + a†.

Return type:

PhysicsExpr

flux_coupling_operator()[source]

Return the number operator used for frequency modulation.

Return type:

PhysicsExpr

tunable_param_names = ()

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

class quchip.FockSpace(levels)[source]

Bases: LocalSpace

Finite Fock ladder with the standard bosonic and qubit operators.

Parameters:

levels (int)

levels: int
property dimension: int

Return the authored local-space dimension.

matrix(name)[source]

Return one named operator as a JAX-compatible dense array.

Parameters:

name (str)

Return type:

Any

operator(name, backend)[source]

Lower one named local operator through backend.

Parameters:
Return type:

Any

class quchip.FluxTunableTransmon(freq=unbound, anharmonicity=unbound, flux_bias=0.0, asymmetry=0.0, *, levels=3, label=None, T1=None, T2=None, thermal_population=None)[source]

Bases: FockDevice

SQUID-dispersion flux-tunable transmon.

The constructor takes the calibrated local physical parameters; SQUID metadata is derived on read and is not part of the public interface.

Parameters:
  • freq (float) – Calibrated local 0 -> 1 transition frequency ω in GHz, at the stored flux_bias. Must be positive. May be a JAX tracer.

  • anharmonicity (float) – Calibrated local anharmonicity α in GHz, at the stored flux_bias. Must be negative (α ≈ −E_C). May be a JAX tracer.

  • flux_bias (float, default 0.0) – Current operating point Φ/Φ₀ and calibration-anchor coordinate. Any real value; the SQUID inversion is undefined only at the symmetric-SQUID degenerate point (asymmetry == 0 and flux_bias a half-integer — see validate()). Rebinding this value alone preserves the inferred SQUID calibration and updates freq. Rebinding it together with freq defines a new anchor. It is a JAX pytree leaf and can be differentiated or swept through the public chip API.

  • asymmetry (float, default 0.0) – SQUID junction asymmetry d = (E_{J1}−E_{J2})/(E_{J1}+E_{J2}). Must be in [0, 1).

  • levels (int, default 3) – Fock-space truncation.

  • label (str | None, default None) – Auto-generated as fluxtunable_{idx} when omitted.

  • **noise_kwargs – Forwarded to BaseDeviceT1, T2, thermal_population.

  • T1 (Any)

  • T2 (Any)

  • thermal_population (Any)

tunable_param_names = ('freq', 'anharmonicity', 'flux_bias')

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

dressed_fit_target_fields = (('freq', 'freq'), ('anharmonicity', 'anharmonicity'))

(dressed_observable, declared_field) pairs used when this device appears in the desired-chip form of fit_a_dress. Empty means that the model makes no automatic dressed-target claim; circuit-level models can remain fixed until the user supplies explicit constraints.

dressed_fit_param_names = ('freq', 'anharmonicity')

Bare parameters normally varied to reproduce dressed_fit_target_fields. This remains separate from tunable_param_names: a model may expose parameters for sweeps without claiming that inverse design can identify all of them from its default dressed observables.

computational = True

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

approximation = 'Duffing-approximated SQUID transmon; adiabatic flux (calibration-anchor, no Landau-Zener).'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

freq: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='\\omega', noise=False, kw_only=False, required=False)
anharmonicity: Scalar = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol='\\alpha', noise=False, kw_only=False, required=False)
flux_bias: Scalar = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit='Phi_0', symbol='\\Phi', noise=False, kw_only=False, required=False)
asymmetry: Scalar = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol='d', noise=False, kw_only=False, required=False)
validate()[source]

Range checks on concrete scalars only; traced values pass unchecked.

Return type:

None

set_parameter_values(values)[source]

Apply flux and calibration overrides without mapping-order effects.

Parameters:

values (Mapping[str, Any])

Return type:

None

tunable_param_bounds(name, value)[source]

Use one SQUID period as the default bound for explicit flux fitting.

Parameters:
Return type:

tuple[float, float]

local_hamiltonian(op, p)[source]

Return the Duffing Hamiltonian built from the calibrated freq and anharmonicity.

H = ω n + (α/2) n(n I). Rebinding flux_bias first updates the stored freq through the anchored SQUID dispersion.

Parameters:
Return type:

PhysicsExpr

frequency_at(flux)[source]

SQUID dispersion ω(Φ/Φ₀) in GHz, using derived E_C and E_J_max.

Parameters:

flux (float) – Reduced flux Φ/Φ₀. JAX-traceable.

Return type:

Any

flux_for_frequency(target_freq)[source]

Inverse SQUID dispersion on the monotonic lobe Φ/Φ₀ ∈ [0, 0.5).

Derivation:

ω(Φ) = sqrt(8 E_C E_J_max sqrt(cos²(πΦ) + d²sin²(πΦ))) − E_C → let S = (ω + E_C)² / (8 E_C E_J_max) → cos²(πΦ)(1 − d²) + d² = S² → cos²(πΦ) = (S² − d²) / (1 − d²)

Raises:

ValueError – If target_freq is concrete and lands outside the frequency range frequency_at() reaches over Φ/Φ₀ ∈ [0, 0.5) at the current calibration anchor. A traced target_freq (or a traced anchor) skips this check; the returned flux clips to the lobe endpoint, so out-of-domain behavior is undefined for traced inputs.

Parameters:

target_freq (Any)

Return type:

Any

physics_notes()[source]

Return declared SQUID-transmon calibration-anchor assumptions.

Return type:

list[str]

class quchip.Fluxonium(E_C, E_J, E_L, phi_ext=0.0, levels=None, label=None, *, num_basis=400, phi_max=15.707963267948966, basis=None, collapse_model='fermi_golden', coupling_channel=None, collapse_rate_threshold=1e-08, **noise)[source]

Bases: DeviceModel

Fluxonium with its Hamiltonian authored in a finite phase-grid basis.

Parameters:
  • E_C (Any)

  • E_J (Any)

  • E_L (Any)

  • phi_ext (Any)

  • levels (int)

  • label (Any)

  • num_basis (int)

  • phi_max (float)

  • basis (Literal['native', 'eigen'] | None)

  • collapse_model (Literal['fermi_golden', 'ladder'])

  • coupling_channel (Literal['charge', 'flux'] | None)

  • collapse_rate_threshold (float)

  • noise (Any)

tunable_param_names = ('E_C', 'E_J', 'E_L', 'phi_ext')

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

approximation = 'Finite phase-grid model with a second-order charge kinetic operator; accuracy is governed by num_basis and phi_max.'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

computational = True

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

requires_projection_levels: ClassVar[bool] = True
structural_setting_names = ('num_basis', 'phi_max', 'basis', 'projection_levels', 'collapse_model', 'coupling_channel', 'collapse_rate_threshold')
E_C: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='E_C', noise=False, kw_only=False, required=False)
E_J: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='E_J', noise=False, kw_only=False, required=False)
E_L: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='E_L', noise=False, kw_only=False, required=False)
phi_ext: Scalar = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol='\\varphi_{\\mathrm{ext}}', noise=False, kw_only=False, required=False)
dissipation(op, p)[source]

Return device-local Lindblad channels.

The base channels implement T1, T2, and thermal occupation. Subclasses may append channels with super().dissipation(op, p).

Parameters:
Return type:

tuple[CollapseChannel, …]

local_space()[source]

Return the authored finite phase-grid space.

Return type:

PhaseGridSpace

local_hamiltonian(op, p)[source]

Return the native fluxonium Hamiltonian in ordinary GHz.

Parameters:
Return type:

PhysicsExpr

property freq: Any

Return the isolated 0-to-1 transition in GHz.

eigenenergies()[source]

Return isolated energies shifted to zero at the ground state.

Return type:

Any

eigenvectors()[source]

Return the isolated energy-ordered phase-grid eigenvectors.

Return type:

Any

charge_coupling_operator()[source]

Return the authored charge operator.

Return type:

Any

phase_coupling_operator()[source]

Return the authored phase operator.

Return type:

Any

flux_coupling_operator()[source]

Return the authored flux-line phase operator.

Return type:

Any

physics_notes()[source]

Return base device notes plus the declared approximation, if any.

Return type:

list[str]

to_dict()[source]

Serialize common device state plus declared parameter values.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Reconstruct the device from to_dict() output.

Parameters:

data (dict[str, Any])

Return type:

Fluxonium

class quchip.KerrCavity(freq=unbound, kerr=unbound, *, levels=30, label=None, T1=None, T2=None, thermal_population=None)[source]

Bases: FockDevice

Kerr-nonlinear resonator supporting cat-qubit stabilisation.

Hamiltonian:

\[H = \omega \, \hat{n} - K \, \hat{n}(\hat{n} - I)\]

The nonlinearity \(K\) shifts the photon-number eigenenergies, making the cavity anharmonic. Combined with a two-photon parametric drive at \(2\omega\), the steady state becomes a cat state with amplitude \(\alpha = \sqrt{\varepsilon_2 / K}\).

Parameters:
  • freq (float) – Cavity frequency \(\omega\) in GHz. Must be positive. May be a JAX tracer for sweeps / gradients.

  • kerr (float) – Kerr nonlinearity \(K\) in GHz. Non-negative; positive value shifts even-photon levels downward. Typically 1–100 MHz in superconducting circuits.

  • levels (int) – Fock-space truncation dimension. Choose at least 4 * (eps2 / K) + 10 to avoid truncation artefacts. Default 30.

  • label (str | None) – Human-readable label. None → auto-generated kerr_cavity_0, kerr_cavity_1, …

  • **noise_kwargs – Forwarded to BaseDevice: T1, T2, thermal_population, etc.

  • T1 (Any)

  • T2 (Any)

  • thermal_population (Any)

Notes

This Hamiltonian is diagonal in the Fock basis and does not itself define a computational subspace. Combined with a two-photon parametric drive, the steady state can be engineered into a cat-code manifold spanned by the even cat state \(|C^+_\alpha\rangle\) and the odd cat state \(|C^-_\alpha\rangle\). Bit-flip errors within that manifold are exponentially suppressed, \(\sim e^{-2|\alpha|^2}\), in the stabilized regime. This class’s inherited Pauli surface (computational is False) addresses the bare Fock |0>, |1> subspace; see physics_notes() for the caveat.

References

Examples

>>> from quchip.devices.kerr_cavity import KerrCavity
>>> cav = KerrCavity(freq=5.0, kerr=1.0, levels=10, label="cav")
>>> cav.freq, cav.kerr, cav.levels
(5.0, 1.0, 10)
tunable_param_names = ('freq', 'kerr')

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

approximation = 'Kerr-nonlinear cavity effective single-mode model; SNAIL/STS-SQUID adiabatically eliminated.'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

computational = False

Whether this device represents a computational qubit, as opposed to e.g. a bus resonator or a coupler element.

freq: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='\\omega', noise=False, kw_only=False, required=False)
kerr: Scalar = Parameter(default=unbound, positive=False, nonnegative=True, serialize=True, unit='GHz', symbol='K', noise=False, kw_only=False, required=False)
local_hamiltonian(op, p)[source]

Return \(H = \omega \hat{n} - K \hat{n}(\hat{n} - I)\).

The Kerr term \(\hat{n}(\hat{n}-I) = \hat{n}^2 - \hat{n}\) gives eigenvalue contributions \(-K n(n-1)\) for the \(n\)-photon Fock state.

Returns:

Declarative expression for the Hermitian operator H = omega*n - K*n*(n-1) (GHz), diagonal in the Fock basis.

Return type:

PhysicsExpr

Parameters:
physics_notes()[source]

Return declared Kerr-cavity approximation notes.

Return type:

list[str]

class quchip.LocalSpace[source]

Bases: ABC

Numerical realization of the operators used by one device model.

abstract property dimension: int

Return the authored local-space dimension.

abstractmethod matrix(name)[source]

Return one named operator as a JAX-compatible dense array.

Parameters:

name (str)

Return type:

Any

operator(name, backend)[source]

Lower one named local operator through backend.

Parameters:
Return type:

Any

class quchip.Resonator(freq=unbound, *, levels=10, label=None, T1=None, T2=None, thermal_population=None, internal_quality_factor=None)[source]

Bases: FockDevice

Linear microwave / photonic resonator — pure harmonic oscillator.

Parameters:
  • freq (float) – Bare cavity frequency ω in GHz. Must be positive. May be a JAX tracer for sweeps / gradients.

  • internal_quality_factor (float | None, optional) – Internal Q referenced to the ordinary frequency freq in GHz. When set, adds a photon-loss Lindblad channel sqrt(2*pi*freq/Q) a with angular decay rate kappa = 2*pi*freq/Q in rad/ns. Must be positive. Like every noise parameter, it may be set after construction or cleared with None; the next simulation reflects the current value.

  • levels (int, default 10) – Fock-space truncation. Choose comfortably above the maximum expected photon occupation.

  • label (str | None, default None) – If omitted, auto-generated as resonator_{idx} via the shared labeling counter.

  • **noise_kwargs – Forwarded verbatim to BaseDeviceT1, T2, thermal_population.

  • T1 (Any)

  • T2 (Any)

  • thermal_population (Any)

Example

>>> from quchip.devices import Resonator
>>> r = Resonator(freq=7.2, internal_quality_factor=10_000, levels=8)
>>> len(r.collapse_operators()) >= 1
True
tunable_param_names = ('freq',)

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

dressed_fit_target_fields = (('freq', 'freq'),)

(dressed_observable, declared_field) pairs used when this device appears in the desired-chip form of fit_a_dress. Empty means that the model makes no automatic dressed-target claim; circuit-level models can remain fixed until the user supplies explicit constraints.

dressed_fit_param_names = ('freq',)

Bare parameters normally varied to reproduce dressed_fit_target_fields. This remains separate from tunable_param_names: a model may expose parameters for sweeps without claiming that inverse design can identify all of them from its default dressed observables.

freq: Scalar = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='GHz', symbol='\\omega', noise=False, kw_only=False, required=False)
internal_quality_factor: Scalar = Parameter(default=None, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=True, kw_only=True, required=False)
approximation = 'Linear harmonic oscillator with no Kerr or cross-Kerr self-interaction.'

Declared approximation-regime statement surfaced by physics_notes() — the mechanism that keeps a model’s stated validity range attached to the class rather than buried in a docstring a caller may not read.

local_hamiltonian(op, p)[source]

Return the harmonic oscillator Hamiltonian H = freq * n.

Parameters:
Return type:

PhysicsExpr

dissipation(op, p)[source]

Return device-local Lindblad channels.

The base channels implement T1, T2, and thermal occupation. Subclasses may append channels with super().dissipation(op, p).

Parameters:
Return type:

tuple[CollapseChannel, …]

physics_notes()[source]

Return declared harmonic-oscillator and dissipation assumptions.

Return type:

list[str]

intrinsic_decay_rate()[source]

Combined lowering-channel rate: κ = 2π·freq/Q photon loss plus the thermal-emission rate.

Both internal_quality_factor and T1/thermal_population build independent lowering-operator collapse channels on this device (the internal_photon_loss channel, a pure loss channel unaffected by thermal_population, and the inherited thermal-emission channel — see intrinsic_decay_rate() for its (n̄+1)/T1 / n̄+1 formulas); this hook reports their summed rate rather than either alone, so a caller reading one scalar decay rate (e.g. an adiabatic-elimination Purcell fold) does not under-count decay when both are set. None only when neither is set.

Return type:

Any | None

class quchip.ChargeCoupled(*args, **kwargs)[source]

Bases: Protocol

Device exposes the physical charge operator in its authored local basis.

ChargeDrive dispatches against this Protocol and emits drives using charge_coupling_operator().

charge_coupling_operator()[source]

Return the physical charge operator in the authored local basis.

Return type:

Any

class quchip.PhaseCoupled(*args, **kwargs)[source]

Bases: Protocol

Device exposes the physical phase-space coupling operator.

Returns \(\sin\hat\varphi\) on a charge-basis transmon (where \(\hat\varphi\) is not single-valued in the integer charge basis) or \(\hat\varphi\) on a fluxonium (where it is well-defined). Used by PhaseDrive.

phase_coupling_operator()[source]

Return the physical phase-space coupling operator in the authored basis.

Return type:

Any

class quchip.PhaseGridSpace(points, extent)[source]

Bases: LocalSpace

Uniform endpoint-excluded phase grid with nonperiodic finite differences.

The centered-difference stencil does not wrap across the grid boundary; values beyond either endpoint are treated as zero.

Parameters:
points: int
extent: float
property dimension: int

Return the authored local-space dimension.

matrix(name)[source]

Return one named operator as a JAX-compatible dense array.

Parameters:

name (str)

Return type:

Any

class quchip.FluxCoupled(*args, **kwargs)[source]

Bases: Protocol

Device exposes the physical flux-line coupling operator.

For a fluxonium this is \(\hat\varphi\). Used by FluxDrive.

flux_coupling_operator()[source]

Return the physical flux-line coupling operator in the authored basis.

Return type:

Any

class quchip.Chip(devices, couplings=None, control_equipment=None, label=None, frame='lab', approximation=RWA(keep_bands=None), basis='native', backend=None, baths=None, ports=None)[source]

Bases: object

Composite quantum system: devices + couplings + (optional) control.

The chip is the primary user-facing object. It exposes:

Methods that accept a device reference take either a device object or its label string; object references are preferred in examples.

Parameters:
  • devices (list[BaseDevice]) – Ordered device list. Tensor-product position equals list index; labels must be unique.

  • couplings (list[BaseCoupling], optional) – Two-body couplings. Each coupling’s referenced devices must be in devices.

  • control_equipment (ControlEquipment, optional) – Aggregates drive lines and signal-chain transforms (crosstalk, delays, gains). Can also be attached later via wire() or connect().

  • label (str, optional) – Human-readable chip label.

  • frame (FrameSpec) –

    Initial frame specification:

    • "lab" — all reference frequencies 0 GHz (default).

    • "rotating" — per-device rotating frame at dressed drive frequencies.

    • scalar-like — one shared reference frequency for all devices.

    • dict — per-device references keyed by label or device.

  • approximation (Approximation) – Engine strategy applied after the complete authored Hamiltonian is assembled. Defaults to RWA.

  • basis ({"native", "eigen"}) – Chip-wide local solver-basis policy. "native" preserves each device’s authored coordinate basis; "eigen" transforms into its retained local energy subspace. A device-level basis overrides this policy.

  • backend (str or Backend, optional) – Chip-specific backend. None uses the process default.

  • baths (list[Bath], optional) – Shared or collective unobserved environments.

  • ports (list[Port], optional) – Accessible Markovian input-output channels. Ports add collapse channels without adding Hilbert-space factors.

Examples

>>> from quchip import DuffingTransmon, Resonator, Capacitive, Chip
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q")
>>> r = Resonator(freq=7.0, levels=5, label="r")
>>> chip = Chip([q, r], couplings=[Capacitive(q, r, g=0.05)])
>>> chip.energy({q: 1})  # dressed |1,0⟩ eigenvalue, GHz
unresolved_hamiltonian()[source]

Return the authored lab-frame static Hamiltonian.

Embeds every device Hamiltonian into the full tensor space and adds each coupling’s embedded interaction. Does not apply the rotating-frame transform or any drive envelopes.

This is the exact authored lab-frame Hamiltonian. RWA and frame transformations are applied only inside the engine. At solve time, the engine subtracts Σ_i ω_ref,i n̂_i for each device, where ω_ref,i is the frame reference resolved by quchip.engine.frames.resolve_frame(). The 2π boundary crossing and the subtraction both live in quchip.engine.assembly._build_static_h0(). Use frame_info() to inspect which reference frequency each device will use.

Returns:

Symbolic Hamiltonian on ⨂_d H_d. Call .matrix() for a dense numerical view using current bindings.

Return type:

PhysicsExpr

hamiltonian()[source]

Return the Hamiltonian after the chip’s engine policies.

Return type:

PhysicsExpr

resolve(*, frame=None, approximation=None)[source]

Return a frozen engine contract without mutating chip intent.

frame=None uses frame; an explicit frame resolves this snapshot only. approximation=None likewise uses the chip default. Neither override mutates chip intent.

Parameters:
Return type:

EngineResult

dynamic_contributions()[source]

Chip-owned time-dependent Hamiltonian contributions with their support.

Returns (local_op, time_dependence, support, owner, origin, tag) tuples: one support index for a device-local operator, two for a coupling’s two-body operator. Operators are lab-frame, ordinary GHz — the engine applies the 2π boundary. Drive terms are not included; they are schedule-owned and enter through engine assembly’s drive compilation.

Return type:

list[tuple[Any, Any, tuple[int, …], Any, str, str]]

collapse_contributions(bases=None)[source]

Every Lindblad collapse operator on the chip, with its support.

Returns (operator, rate, support, source, channel, parameter_paths) tuples: one device index for a device- or drive-line-local operator, two for a coupling’s two-body operator, and an empty tuple for an operator already embedded in the full space (baths). Rates are in 1/ns, Lindblad-ready — each component owns its rate physics, including any intrinsic 2π (e.g. a resonator’s κ = 2π·f/Q).

Every returned operator is authored locally and transformed by the engine into the same fixed local solver bases as the Hamiltonian. This is the standard local-Lindblad approximation (Breuer & Petruccione, The Theory of Open Quantum Systems, Oxford, 2002, Ch. 3) rather than a dressed-basis (polaron-frame) master equation, and applies chip-wide regardless of which components carry noise — see the "chip" entry of physics_notes().

Parameters:

bases (Mapping[str, Any] | None)

Return type:

list[tuple[Any, Any, tuple[int, …], str, str, tuple[str, …]]]

property device_map: dict[str, BaseDevice]

Label → device mapping (the chip’s own dict; do not mutate).

property coupling_map: dict[str, BaseCoupling]

Couplings by label, insertion-ordered (do not mutate).

coupling(coupling)[source]

Return a coupling by label or object.

Parameters:

coupling (str | BaseCoupling)

Return type:

BaseCoupling

property backend: Backend

per-call override > chip-specific > process default.

The per-call override is the backend= argument of simulate() / simulate_batch (scoped through quchip.backend._backend_context()); it outranks even a chip-constructed backend so one chip can serve, e.g., QuTiP sweeps and dynamiqs gradient solves without global state flips.

Type:

Active backend

property frame: FrameSpec

Current frame specification (see set_frame()).

property approximation: Approximation

Default engine approximation strategy.

property control_equipment: ControlEquipment | None

Attached control equipment, if any.

property devices: tuple[BaseDevice, ...]

Ordered tuple of devices; index matches tensor-product position.

property couplings: tuple[BaseCoupling, ...]

Tuple of couplings in insertion order.

property dims: tuple[int, ...]

Per-device Hilbert-space dimensions (same order as devices).

property authored_dims: tuple[int, ...]

Per-device dimensions of the exact authored local spaces.

property basis: Literal['native', 'eigen']

Chip-wide local solver-basis policy inherited by devices.

resolve_basis(device)[source]

Resolve a device override against the chip basis policy.

Parameters:

device (str | BaseDevice)

Return type:

Literal[‘native’, ‘eigen’]

property total_dim: int

Total Hilbert-space dimension (product of per-device dims).

property baths: tuple[Bath, ...]

Chip-level baths (shared/collective dissipation), insertion order.

property ports: tuple[Port, ...]

Accessible Markovian input-output channels, in declaration order.

port(port)[source]

Return one declared port by object or label.

Parameters:

port (str | Port)

Return type:

Port

add_bath(bath)[source]

Attach a bath to this chip and return it (for fluent use).

Baths may be added at any time after construction — the next simulate/solve collects the bath’s collapse operators automatically, no rebuild needed. The bath is validated immediately: a non-Bath argument, a target label not on this chip, or a label colliding with an already-attached bath fails here rather than cryptically at solve time (or, for a label collision, silently overwriting an entry on the physics_notes() audit surface).

Parameters:

bath (Bath)

Return type:

Bath

set_noise(config=None, *, baths=None)[source]

Replace this chip’s entire noise description in one call.

The call is the chip’s complete noise state: for every device, each noise parameter (see noise_parameter_names()) is set from config when given and reset to None otherwise, and the chip’s baths become exactly baths (omitted → no baths). chip.set_noise() therefore clears all noise. Re-running the same call is a silent no-op, so notebook cells converge instead of stacking duplicate baths.

Only dissipation knobs are reachable — a key that is not a noise parameter of that device (e.g. freq) raises, so an optimized Hamiltonian cannot be perturbed by this call. The full target state is validated before anything is written (the same checks the constructors run); on error the chip is left exactly as it was. Applied changes are printed one per line; a no-op prints nothing.

Custom dissipation is supported by declaring it on the device class beforehand with a parameter(noise=True) rate field and a dissipation() override. The declared rate is then an ordinary noise parameter here: sweepable, differentiable, serializable. Runtime closure-style channels are deliberately not supported — their rates could be neither swept, nor differentiated, nor serialized.

Parameters:
  • config (mapping, optional) – {device_or_label: {noise_param: value}}. Devices may appear as objects or labels, once each.

  • baths (list[Bath], optional) – The chip’s complete new bath list (validated like add_bath()).

Return type:

None

property crosstalks: list[Crosstalk]

Convenience view — Crosstalk entries from the signal chain.

device_index(label)[source]

Tensor-product index of a device. Accepts a label string or object.

Parameters:

label (str | BaseDevice)

Return type:

int

plot_graph(path='chip_topology.html', *, full=True, exclude=None, values='bare', **kwargs)[source]

Render chip topology — delegates to quchip.viz.chip.

Parameters:
Return type:

str

plot_energy_levels(*, ax=None, **kwargs)[source]

Render dressed spectrum — delegates to quchip.viz.chip.

Parameters:
Return type:

Any

set_frame(frame)[source]

Set the frame used when assembling simulation inputs.

Supported values:

  • "lab" — all reference frequencies are 0.0 GHz.

  • "rotating" — per-device references use dressed drive frequencies.

  • scalar-like — shared reference frequency for all devices.

  • dict — per-device references keyed by label or device.

Frame changes never alter dressed-state data — dressing is always computed from the lab-frame static Hamiltonian.

Parameters:

frame (FrameSpec)

Return type:

None

property analysis: ChipAnalysis

Dressed-state analysis namespace — the chip’s ChipAnalysis.

Canonical entry point for the full dressed-analysis surface (power users, less-common methods). The common quantities are also exposed as flat chip.* forwarders — energy(), freq(), dress(), dispersive_shift(), … — which delegate here; reach for chip.analysis for everything else.

dress(*, overlap_threshold=0.5, force=False, labeling='DE')[source]

Compute (or retrieve) the dressed-state decomposition. See ChipAnalysis.dress().

Parameters:
Return type:

DressedResult

property is_dressed: bool

Whether a valid dressed-state result is cached. See ChipAnalysis.is_dressed.

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

Dressed eigenenergy (GHz) for a bare-state label. See ChipAnalysis.energy().

Parameters:
Return type:

float

dressed_spectrum()[source]

Raw dressed eigenvalue array without Python scalar coercion. See ChipAnalysis.dressed_spectrum().

Return type:

Any

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

Dressed-state index matching a bare-state label, or None. See ChipAnalysis.dressed_index().

Parameters:
Return type:

int | None

bare_label(dressed_index)[source]

Bare-state label assigned to a dressed-state index. See ChipAnalysis.bare_label().

Parameters:

dressed_index (int)

Return type:

tuple[int, …]

operator_in_dressed_basis(device, op, *, truncate=None)[source]

Embedded device operator transformed to the dressed eigenbasis.

See ChipAnalysis.operator_in_dressed_basis().

Parameters:
Return type:

Any

drive_matrix_elements(transition, *, drives=None)[source]

Return <final~|D_j|initial~> for wired drive lines.

The final dressed state is the row index and the initial dressed state is the column index. In the weak-drive projection these matrix elements set the effective driven-Hamiltonian coefficients. See E. Magesan and J. M. Gambetta, Phys. Rev. A 101, 052308 (2020), DOI 10.1103/PhysRevA.101.052308, and ChipAnalysis.drive_matrix_elements() for the transition shorthand, parameters, return type, and error conditions.

Parameters:
Return type:

LabelKeyedDict

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

Leading bare-basis probabilities for a dressed eigenstate. See ChipAnalysis.state_components().

Parameters:
Return type:

dict[tuple[int, …], float]

dispersive_shift(device_a, device_b)[source]

Dressed cross-Kerr shift (GHz): E(1,1) E(1,0) E(0,1) + E(0,0).

See ChipAnalysis.dispersive_shift().

Parameters:
Return type:

float

static_zz(device_a, device_b)

Dressed cross-Kerr shift (GHz): E(1,1) E(1,0) E(0,1) + E(0,0).

See ChipAnalysis.dispersive_shift().

Parameters:
Return type:

float

zz(device_a, device_b)

Dressed cross-Kerr shift (GHz): E(1,1) E(1,0) E(0,1) + E(0,0).

See ChipAnalysis.dispersive_shift().

Parameters:
Return type:

float

kerr_matrix()[source]

Return the labeled dressed self-Kerr and cross-Kerr matrix in GHz.

See ChipAnalysis.kerr_matrix().

Return type:

KerrMatrix

dressed_anharmonicity(device)[source]

Dressed anharmonicity of one device with others grounded (GHz).

See ChipAnalysis.dressed_anharmonicity().

Parameters:

device (str | BaseDevice)

Return type:

float

transition_frequency(target, lower, upper, when=None)[source]

Return a dressed transition in GHz with optional spectator levels.

Unspecified spectators are in their ground state. target and entries in when accept either device objects or labels.

Parameters:
Return type:

Any

effective_subspace_hamiltonian(states)[source]

Dressed effective Hamiltonian in a labeled bare subspace.

See ChipAnalysis.effective_subspace_hamiltonian().

Parameters:

states (list[Mapping[str | BaseDevice, int] | tuple[int, ...]] | tuple[Mapping[str | BaseDevice, int] | tuple[int, ...], ...])

Return type:

Any

freq(target: None = None, when: dict[str | BaseDevice, int] | None = None) dict[str, float][source]
freq(target: str | BaseDevice, when: dict[str | BaseDevice, int] | None = None) float

All dressed 0→1 frequencies (GHz), or one conditional transition. See ChipAnalysis.freq().

Overloaded: no target returns the full {label: freq} dict; a single target (label or device) returns one scalar 0→1 frequency. Under jax.jit the scalar is a traced 0-d array; the overload is type-only and preserves traceability.

Parameters:
Return type:

dict[str, float] | float

frame_info()[source]

Per-device frame reference frequency ω_ref,i (GHz). See ChipAnalysis.frame_info().

Return type:

dict[str, Any]

physics_notes()[source]

Aggregate physics_notes() across every component.

Returns a dict keyed "chip" for the chip-level entry, and "<kind>:<label>"kind one of "device", "coupling", "drive", "bath", "port" — for every component, mapping to that component’s declared approximations: Hilbert truncation, model regime, RWA status, noise-channel selection, and any other non-obvious assumption the component explicitly declares. Keys are kind-qualified rather than bare labels because the label namespaces are not globally disjoint — a device, coupling, drive, and bath may share a label — and this is an audit surface, so one component’s entry silently overwriting another’s is unacceptable. Drives are enumerated from control_equipment’s wiring rather than per-device connected_drives, so an edge-target ParametricDrive (pumping a coupling, not a device) is included too. The chip-level entry keyed "chip" states the local-Lindblad approximation every collapse operator this chip assembles is built under (see collapse_contributions()) — present even with no baths, since it applies regardless. Intended for inspection/audit rather than for runtime dispatch.

Return type:

dict[str, list[str]]

to_dict()[source]

Serialize chip topology into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct a chip from to_dict() output.

Parameters:

d (dict[str, Any])

Return type:

Chip

clone()[source]

Isolated structural clone suitable for sweep evaluation.

Return type:

Chip

property parameters: Mapping[str, Any]

Bindable numerical values keyed by stable component paths.

property settings: Mapping[str, Any]

Read-only structural choices that are not numerical fit parameters.

with_params(bindings)[source]

Return an isolated structural copy with component-owned values rebound.

Parameters:

bindings (Mapping[str, Any])

Return type:

Chip

partition()[source]

Split into independent sub-chips along the independence graph.

Couplings, non-separable bath target sets, and drive-crosstalk pairs all count as connections. Exact — the joint solve factorizes as the tensor product of the component solves. simulate/seq.simulate consult this automatically; call it directly to orchestrate solves yourself.

Return type:

PartitionResult

status()[source]

Print a lightweight diagnostic dashboard for the chip.

Return type:

None

from_array(data, device=None)[source]

Build a backend operator from a raw NumPy array.

With device, the array is interpreted as a local operator on that device’s subspace and embedded into the full tensor-product space. With device=None the array must already span the full chip Hilbert space.

Parameters:
Return type:

Any

observable(device, op)[source]

Embed a device operator onto the full chip Hilbert space.

Accepts either an operator name ("X", "Y", "Z", "n", "a", "a_dag", "I") or an already-built local-space operator, and returns it embedded on the chip’s tensor-product space.

This is for manual full-space operator construction and analysis; it is not a solver e_op. For solver expectation values use e_ops(), which keeps operators local so the demodulation pipeline can band-decompose and embed them correctly.

Parameters:
Return type:

Any

e_ops(*, correlators=None, **specs)[source]

Build a dict-form e_ops mapping for the solver pipeline.

Each keyword maps a device label to an operator specification: a name string, a list of names, a raw local-space operator, or a mixed list of strings and operators. Two-device correlators (e.g. ⟨Z₁⊗Z₂⟩) are specified via correlators as device-label pairs → operator pairs. Returns local-space operators (not embedded) — the demodulation pipeline embeds as needed.

Examples

>>> from quchip import DuffingTransmon, Capacitive, Chip
>>> q1 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q1")
>>> q2 = DuffingTransmon(freq=5.2, anharmonicity=-0.22, levels=3, label="q2")
>>> chip = Chip([q1, q2], couplings=[Capacitive(q1, q2, g=0.02)])
>>> e = chip.e_ops(
...     q1=["X", "Y", "Z"], q2=["X", "Y", "Z"],
...     correlators={("q1", "q2"): ("Z", "Z")},
... )
Parameters:
Return type:

dict[str | tuple[str, str], Any]

set_state_order(*devices, levels=None)[source]

Declare the device order used to parse string-state shorthands.

After this is called, bare_state(), state(), and superposition() accept single-string specifications where each character is one level per device in devices order. Level symbols default to g=0, e=1, f=2, h=3; digits 0..9 are always accepted as energy-level indices.

Every chip device must be named exactly once.

Examples

>>> from quchip import DuffingTransmon, Resonator, Chip
>>> qb = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="qb")
>>> tc = DuffingTransmon(freq=5.5, anharmonicity=-0.20, levels=3, label="tc")
>>> cr = Resonator(freq=7.0, levels=4, label="cr")
>>> chip = Chip([qb, tc, cr])
>>> chip.set_state_order(qb, tc, cr)
>>> _ = chip.bare_state("eg1")  # {qb: 1, tc: 0, cr: 1}
Parameters:
Return type:

None

superposition(*components)[source]

Normalized bare-basis superposition of tensor-product states.

Each component is either a bare-state spec (dict keyed by device or label, or a string when set_state_order() has been called) or an (amplitude, spec) tuple for weighted mixing. Uniform weights by default; results are normalized to unit norm.

Unlike state(), this stays in the bare product basis — no dressed diagonalization — so the probe basis is explicit.

Examples

>>> import numpy as np
>>> from quchip import DuffingTransmon, Resonator, Chip
>>> qb = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="qb")
>>> cr = Resonator(freq=7.0, levels=4, label="cr")
>>> chip = Chip([qb, cr])
>>> _ = chip.superposition({qb: 0}, {qb: 1})  # equal |00> + |10>
>>> _ = chip.superposition(                   # weighted mix
...     (np.sqrt(0.3), {qb: 1, cr: 0}),
...     (np.sqrt(0.7), {qb: 1, cr: 1}),
... )
Parameters:

components (Mapping[str | BaseDevice, int] | str | tuple[Any, Any])

Return type:

Any

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

Dressed eigenstate assigned from the given product-state level labels.

Accepts a string shorthand (e.g. "eg1") when set_state_order() has been called.

Safe inside jax.jit/grad/vmap: under tracing the assigned eigenvector column is selected through the label_eigensystem() array kernel, so dressed initial states are differentiable end-to-end. The global phase is gauge-dependent (eigh column convention) — populations and |overlap| figures of merit are unaffected.

Parameters:
Return type:

Any

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

Product state from per-device energy levels or authored local kets.

Each device may be specified as either an energy-level index (int) or a ket vector in that device’s authored local space. Devices not mentioned default to the ground state (level 0). Unlike state() this does not diagonalize the coupled system.

Accepts a string shorthand (e.g. "eg1") when set_state_order() has been called.

Parameters:
Return type:

Any

wire(*lines, signal_chain=None)[source]

Attach or replace classical control wiring.

Preferred user-facing API for attaching control to a chip. If lines is omitted, the existing connected lines are reused and only the signal chain is replaced.

Examples

>>> # chip.wire(drive_q, drive_r, signal_chain=[Crosstalk(drive_q, drive_r, c=0.05)])
Parameters:
Return type:

ControlEquipment

unwire(line)[source]

Remove one control line and every signal-chain transform referencing it.

The inverse of wire() for a single line. Accepts the drive object or its label. Returns the removed drive so it can be rewired later. Removing the last line detaches the equipment entirely (control_equipment becomes None).

Parameters:

line (BaseDrive | str)

Return type:

BaseDrive

connect(control_equipment)[source]

Attach control equipment to this chip (low-level API).

Validates every drive target and rejects duplicate drive labels, then reconnects each drive to this chip’s canonical device or coupling instance. User-facing code should prefer wire().

Parameters:

control_equipment (ControlEquipment)

Return type:

None

disconnect()[source]

Detach control equipment entirely (low-level API).

The inverse of connect()/wire(): removes all lines and the signal chain at once (control_equipment becomes None). Returns the detached equipment so it can be reconnected later.

Returns:

The equipment that was attached before detachment.

Return type:

ControlEquipment

solve(problem, *, check_truncation=True, truncation_threshold=0.001)[source]

Solve a typed SolveProblem through this chip’s backend.

Routes through the common solve_problem() chokepoint, so the Hilbert-truncation safety net applies by default; pass check_truncation=False to opt out or retune truncation_threshold.

Parameters:
Return type:

SimulationResult

solve_many(batch_or_problems, *, progress=True)[source]

Solve a SolveBatch or list of problems.

Chip-level validation only enforces what needs self (every input was built for this chip); the input-shape dispatch and batching are delegated to quchip.engine.solve_many(), which owns the single SolveBatch / list dispatch.

Parameters:
  • batch_or_problems (Any)

  • progress (bool)

Return type:

SimulationBatchResult

steadystate(*, e_ops=None, options=None, frame=None, approximation=None)[source]

Solve this chip’s unique static Lindblad steady state.

Parameters:
Return type:

SteadyStateResult

steadystate_batch(*axes, e_ops=None, options=None, frame=None, approximation=None, progress=True)[source]

Solve static Lindblad steady states over parameter sweep axes.

Parameters:
Return type:

Any

describe()[source]

Sectioned plain-text report of everything on the chip.

Devices with their declared parameters (units included), noise settings, couplings, control wiring, and baths — the “what did I just build?” view. Returns a string; print(chip.describe()). Traced parameters render as <traced> and are never concretized.

Return type:

str

class quchip.Capacitive(device_a, device_b, g=unbound, *, label=None)[source]

Bases: CouplingModel

Capacitive (charge-charge) coupling between two devices.

The capacitive interaction between two electromagnetic modes takes the canonical dipole-dipole form in the raising/lowering basis:

  • Full form: H_int = g · (a + a†)(b + b†)

  • Band-RWA form: H_int = g · (a†b + a b†) (derived, not authored)

The coupling authors only the full form; RWA retaining the Δa + Δb == 0 bands of it, which is exactly g · (a†b + a b†). The RWA drops the counter-rotating terms a b and a† b†, valid when ω_a + ω_b g — the sum-frequency condition that makes those terms fast-rotating and hence negligible. This is distinct from the dispersive condition |ω_a ω_b| g, which instead governs whether the retained exchange term g · (a†b + a b†) can be treated perturbatively (see TunableCapacitive / eliminate() for the dispersive reduction). Approximation is selected on the chip or for one solve, never on the coupling.

Parameters:
  • device_a (BaseDevice or str) – The two coupled devices, given as objects or label strings. Label-string references are late-bound via Chip.

  • device_b (BaseDevice or str) – The two coupled devices, given as objects or label strings. Label-string references are late-bound via Chip.

  • g (float) – Coupling strength in GHz. May be a traced JAX scalar for sweeps / autodiff.

  • label (str, optional) – Human-readable label; defaults to "cap_{n}".

References

Blais et al., PRA 69, 062320 (2004), Eq. 11. Krantz et al., Appl. Phys. Rev. 6, 021318 (2019), §V.B. Blais et al., Rev. Mod. Phys. 93, 025005 (2021), §II.B.

Examples

>>> from quchip import DuffingTransmon, Resonator, Capacitive
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q")
>>> r = Resonator(freq=7.0, levels=5, label="r")
>>> coupling = Capacitive(q, r, g=0.05)
folds_exchange: ClassVar[bool] = True
reduces_to_crosskerr: ClassVar[bool] = True
default_fit_observable: ClassVar[str] = 'cross_kerr'

Observable represented by this coupling’s declared scalar when the enclosing chip is passed to fit_a_dress as a dressed specification. Concrete coupling models override this when their inverse-design quantity is a dressed interaction observable.

g: Any = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol='g', noise=False, kw_only=False, required=False)
default_dressed_target()[source]

Use exchange rate for an edge between non-computational modes.

Return type:

tuple[str, Any]

interaction(a, b, p)[source]

Return the full capacitive interaction g * (a + a†)(b + b†).

Parameters:
Return type:

PhysicsExpr

physics_notes()[source]

Return the declared capacitive interaction.

Return type:

list[str]

classmethod from_dict(d, device_a, device_b)[source]

Reconstruct a capacitive coupling from serialized state.

Parameters:
Return type:

Capacitive

class quchip.Coupling(device_a, device_b, g, *, op_a=None, op_b=None, interaction=None, label=None)[source]

Bases: BaseCoupling

Generic two-body coupling with a user-supplied interaction.

Use this escape hatch when no concrete coupling class models the desired physics (inductive, longitudinal, cross-Kerr test forms, synthetic spin-spin couplings, photonics-style beam-splitters, …). The user supplies the operator structure; this class only provides the g scaling, RWA pass-through, and bookkeeping.

Two mutually exclusive modes:

Product formH_int = g · op_a(device_a) op_b(device_b):

Coupling(q, r, g=0.02,
    op_a=lambda d: d.number_operator(),
    op_b=lambda d: d.number_operator())

Callable formH_int = g · interaction(device_a, device_b, backend):

Coupling(q, r, g=0.02,
    interaction=lambda a, b, bk: (
        bk.tensor(bk.dag(a.lowering_operator()), b.lowering_operator())
        + bk.tensor(a.lowering_operator(), bk.dag(b.lowering_operator()))
    ))

The selected chip approximation is applied to the complete user-supplied operator after authored physics is assembled.

Parameters:
property coupling_strength: float

Scalar prefactor g supplied by the user.

interaction_hamiltonian()[source]

User-defined interaction on H_a H_b, scaled by g.

Return type:

Any

physics_notes()[source]

Return notes describing the user-supplied interaction mode.

Return type:

list[str]

to_dict()[source]

Reject serialization because callables cannot be made persistent.

Return type:

dict[str, Any]

classmethod from_dict(d, device_a, device_b)[source]

Reject deserialization because callables cannot be reconstructed.

Parameters:
Return type:

Coupling

class quchip.CrossKerr(device_a, device_b, chi=unbound, *, label=None)[source]

Bases: CouplingModel

Cross-Kerr (dispersive) coupling H_int = χ · n̂_a n̂_b.

The effective diagonal interaction left when an exchange coupling is reduced in the dispersive regime — the natural coupling for effective readout chips (qubit + resonator + CrossKerr probed by an ordinary charge line) and static-ZZ modelling. Diagonal in both endpoints, so the RWA and full forms coincide and the term is frame-trivial.

Declared approximation: this is a uniform-pull model — one χ per edge, the same shift per endpoint excitation; per-level χ differences, dispersive breakdown at the critical photon number, and Purcell decay are not represented (fold Purcell into endpoint T1 via eliminate() when it matters).

Parameters:
  • device_a (BaseDevice or str) – The two coupled devices, as objects or label strings.

  • device_b (BaseDevice or str) – The two coupled devices, as objects or label strings.

  • chi (float) – Cross-Kerr shift in GHz per excitation pair, sign included (convention: full pull E₁₁ E₁₀ E₀₁ + E₀₀). May be a JAX tracer.

  • label (str, optional) – Defaults to "crosskerr_{n}".

is_effective: ClassVar[bool] = True
default_fit_observable: ClassVar[str] = 'cross_kerr'

Observable represented by this coupling’s declared scalar when the enclosing chip is passed to fit_a_dress as a dressed specification. Concrete coupling models override this when their inverse-design quantity is a dressed interaction observable.

chi: Any = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol='\\chi', noise=False, kw_only=False, required=False)
interaction(a, b, p)[source]

Full form χ · n̂_a n̂_b (diagonal; identical under RWA).

Parameters:
Return type:

PhysicsExpr

parametric_interaction(a, b, p)[source]

Modulable structure n̂_a n̂_b — δχ(t) pumps ride this.

Parameters:
Return type:

PhysicsExpr

physics_notes()[source]

Return the declared dispersive-approximation provenance.

Return type:

list[str]

class quchip.TunableCapacitive(device_a, device_b, g_0=unbound, *, label=None)[source]

Bases: CouplingModel

Capacitive coupling with a scheduled parametric pump.

Effective two-body interaction with a static mean coupling strength:

\[H_{\text{int}} \;=\; g_0\,\hat Q_a\hat Q_b\]

where \(\hat Q\) is each endpoint’s physical charge-like coupling operator (the position quadrature for a Fock model). The engine applies any requested RWA after local-basis materialization. \(g_0\) is the static coupling strength in GHz; it may be a JAX tracer and flows through jax.grad() without concretization.

Time-dependence is not a construction-time parameter: a ParametricDrive wired onto this coupling schedules a pump δ(t) via pump(), multiplying the same operator structure the static term uses (parametric_interaction()).

Parameters:
  • device_a (BaseDevice) – The two coupled devices.

  • device_b (BaseDevice) – The two coupled devices.

  • g_0 (float) – Static (mean) coupling strength in GHz. May be a JAX tracer.

  • label (str, optional) – Human-readable label; defaults to "tunable_cap_{n}".

Notes

The pump multiplies parametric_interaction(); frame and RWA logic stay in the engine. A pump tone at the qubits’ difference frequency |ω_a ω_b| activates the parametric beam-splitter / iSWAP exchange, while a tone at the sum frequency ω_a + ω_b instead activates two-mode-squeezing (a†b†) terms. Either is expressed via the drive’s freq argument, not a coupling-side carrier.

References

McKay, Filipp, Mezzacapo, Magesan, Chow & Gambetta, Universal Gate for Fixed-Frequency Qubits via a Tunable Bus, Phys. Rev. Applied 6, 064007 (2016) — parametric two-qubit gates via coupler flux modulation.

Krantz et al., A quantum engineer’s guide to superconducting qubits, Appl. Phys. Rev. 6, 021318 (2019), §V.D — tunable couplers.

Examples

>>> from quchip import Chip, ControlEquipment, DuffingTransmon, ParametricDrive, TunableCapacitive
>>> q0 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q0")
>>> q1 = DuffingTransmon(freq=5.2, anharmonicity=-0.22, levels=3, label="q1")
>>> tc = TunableCapacitive(q0, q1, g_0=0.0, label="tc")
>>> pump = ParametricDrive(tc, label="pump")
>>> chip = Chip([q0, q1], couplings=[tc], control_equipment=ControlEquipment([pump]))
>>> # seq.pump(tc, envelope=..., freq=...) schedules δg(t); see QuantumSequence.pump.
is_effective: ClassVar[bool] = True
folds_exchange: ClassVar[bool] = True
reduces_to_crosskerr: ClassVar[bool] = True
default_fit_observable: ClassVar[str] = 'cross_kerr'

Observable represented by this coupling’s declared scalar when the enclosing chip is passed to fit_a_dress as a dressed specification. Concrete coupling models override this when their inverse-design quantity is a dressed interaction observable.

g_0: Any = Parameter(default=unbound, positive=False, nonnegative=False, serialize=True, unit='GHz', symbol='g_0', noise=False, kw_only=False, required=False)
interaction(a, b, p)[source]

Static contribution g_0 · (a + a†)(b + b†).

Parameters:
Return type:

PhysicsExpr

parametric_interaction(a, b, p)[source]

Modulable charge-charge structure a scheduled pump multiplies.

Parameters:
Return type:

PhysicsExpr

physics_notes()[source]

Return the tunable-coupling and effective-model assumptions.

Return type:

list[str]

class quchip.DressedResult(eigenvalues, state_map, dressed_eigenvalues, assignment_overlaps, hybridized_labels, bare_labels, bare_labels_by_dressed_index, eigenvector_matrix=None, overlap_threshold=0.5, labeling='DE', _eigensystem=None)[source]

Bases: object

Store a frozen snapshot of a dressed-state diagonalization.

Parameters:
eigenvalues

Sorted dressed eigenvalues in GHz.

Type:

array-like

eigenstates

Backend eigenstate objects in the same order as eigenvalues. Materialized lazily on first access from the underlying EigensystemData — the dressing / sweep hot path never touches them, so the per-column backend kets are only built when a caller actually asks for states.

Type:

array-like

state_map

Assignment from bare product-basis label (one int per device) to the dressed eigenstate index it overlaps most with.

Type:

dict[tuple[int, …], int]

dressed_eigenvalues

Dressed eigenvalue for each assigned bare label — direct lookup path for Chip.energy().

Type:

dict[tuple[int, …], Any]

assignment_overlaps

|⟨bare|dressed⟩|² of each assignment; values below overlap_threshold flag hybridization.

Type:

dict[tuple[int, …], float]

hybridized_labels

Bare labels whose assignment quality is below overlap_threshold. A non-empty tuple triggers a user warning at dress time.

Type:

tuple[tuple[int, …], …]

bare_labels

Product energy-level labels exposed by the chip’s resolved local dimensions, in chip order.

Type:

tuple[tuple[int, …], …]

bare_labels_by_dressed_index

Inverse of state_map — dressed index → assigned bare label.

Type:

dict[int, tuple[int, …]]

eigenvector_matrix

Columns = dressed eigenvectors in the bare product basis. Used for ChipAnalysis.operator_in_dressed_basis() and ChipAnalysis.state_components().

Type:

array-like or None

overlap_threshold

Minimum overlap for a confident assignment.

Type:

float

labeling

Algorithm id (currently only "DE"), resolved to the confidence-ordered row-greedy overlap-matching policy actually run by ChipAnalysis.dress() (assign_rowwise_greedy()).

Type:

str

eigenvalues: Any
state_map: dict[tuple[int, ...], int]
dressed_eigenvalues: dict[tuple[int, ...], Any]
assignment_overlaps: dict[tuple[int, ...], float]
hybridized_labels: tuple[tuple[int, ...], ...]
bare_labels: tuple[tuple[int, ...], ...]
bare_labels_by_dressed_index: dict[int, tuple[int, ...]]
eigenvector_matrix: Any = None
overlap_threshold: float = 0.5
labeling: str = 'DE'
property eigenstates: Any

Backend eigenstate kets, materialized lazily from the eigensystem.

class quchip.KerrMatrix(labels, values)[source]

Bases: object

Labeled dressed self-Kerr and cross-Kerr coefficients in GHz.

labels follows chip device order. values is a real symmetric square array: diagonal entries are dressed anharmonicities and off-diagonal entries are full-pull cross-Kerr shifts.

Parameters:
labels: tuple[str, ...]
values: Any
class quchip.Bath(recipe, targets=None, *, temperature=None, rate=None, correlated=False, label=None)[source]

Bases: object

A shared environment coupling a set of devices to a common bath.

Attach at construction (Chip(..., baths=[...])) or at any time after via add_bath() — the next simulate/solve collects the bath’s collapse operators automatically.

Parameters:
  • recipe (str) – Built-in collapse-channel model. One of "thermal", "collective_decay", or "correlated_dephasing". The argument name is retained for API and serialization compatibility.

  • targets (list[BaseDevice | str] | None) – Devices the bath couples to (objects or labels). None (default) means every device in the chip — natural for a global thermal bath.

  • temperature (float | None) – Bath temperature in mK (required for "thermal"). May be a JAX tracer for sweeps / gradients.

  • rate (float | None) – Bath–device coupling rate γ in 1/ns. For "thermal" it is the environmental coupling rate (explicit — never silently borrowed from a device T1, so it cannot double-count device-level noise). For the collective models it is the overall jump rate. None defaults to 1.0 (user controls the absolute scale elsewhere).

  • correlated (bool) – "thermal" only: False (default) emits independent per-device channels sharing one temperature. True is unsupported and raises NotImplementedError. The collective models always emit a single correlated operator regardless of this flag.

  • label (str | None) – Auto-generated "bath_{n}" when omitted.

Examples

>>> from quchip import DuffingTransmon, Chip, Bath
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q")
>>> chip = Chip([q])
>>> _ = chip.add_bath(Bath("thermal", temperature=20.0))  # global 20 mK bath
>>> _ = chip.add_bath(Bath("collective_decay", targets=[q], rate=0.01))
resolve_targets(chip)[source]

Return the ordered target device labels (defaults to all devices).

Parameters:

chip (Chip)

Return type:

list[str]

property separable: bool

Whether this bath factorizes into independent per-target channels.

True for models that emit one collapse operator per target ("thermal" with independent channels); False for models that emit a single jump operator summed over targets ("collective_decay", "correlated_dephasing"). Partitioning treats a non-separable bath’s target set as one inseparable block.

to_dict()[source]

Serialize the bath; targets are stored as label strings.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct a bath from serialized state (targets as label strings).

Parameters:

d (dict[str, Any])

Return type:

Bath

physics_notes()[source]

Return human-readable declarations of this bath’s model and scope.

Mirrors physics_notes(): one entry naming the model and its targets, plus a model-specific assumption a user of this bath should be aware of.

Return type:

list[str]

copy()[source]

Independent copy of this bath (targets normalize to label strings).

Used by Chip.clone and eliminate so a transformed chip never shares live Bath objects with its source — mutating one chip’s bath must not silently change another chip’s physics. Parameter values (temperature, rate) are carried by reference, so traced values stay traced.

Return type:

Bath

parameter_values()[source]

Return active bath values by local field name.

Return type:

dict[str, Any]

set_parameter_value(name, value)[source]

Apply one bath value on an isolated bath copy.

Parameters:
Return type:

None

dissipation(chip, bases=None)[source]

Return authored full-chip collapse channels.

"thermal" emits independent per-target relaxation/absorption pairs sharing one bath temperature (_bose()). The two collective models instead each emit a single jump operator summed over the resolved targets:

  • "collective_decay": L = sum_i a_i at rate gamma — an equal-phase, equal-weight rank-one collective channel, not general collective (super/subradiant) decay, which requires per-pair phase and weight factors set by the target geometry (Lehmberg, Phys. Rev. A 2, 883 (1970), for the general collective-radiative-decay construction).

  • "correlated_dephasing": L = sum_i n_i at rate gamma — maximally correlated common-mode dephasing (every target shares the identical dephasing fluctuation), not general correlated dephasing with a target-dependent correlation structure (Breuer & Petruccione, The Theory of Open Quantum Systems, Oxford, 2002, Ch. 3, for the general Lindblad construction).

Contributions remain backend-neutral; the engine projects and lowers them with the same basis records used for Hamiltonian terms.

Parameters:
  • chip (Chip)

  • bases (Mapping[str, Any] | None)

Return type:

tuple[CollapseChannel, …]

collapse_channels(chip, bases=None)[source]

Return normalized full-chip bath channels.

Parameters:
  • chip (Chip)

  • bases (Mapping[str, Any] | None)

Return type:

tuple[CollapseChannel, …]

class quchip.Port(target, *, rate=None, external_quality_factor=None, operator=None, phase=0.0, label=None)[source]

Bases: object

One accessible Markovian channel with a dimensionless coupling operator.

Parameters:
  • target (Any | Sequence[Any])

  • rate (Any)

  • external_quality_factor (Any)

  • operator (Any)

  • phase (Any)

  • label (str | None)

resolve_targets(chip)[source]

Return target labels after checking that they belong to chip.

Parameters:

chip (Chip)

Return type:

tuple[str, …]

rate_value(chip)[source]

Return the external coupling rate in 1/ns.

Parameters:

chip (Chip)

Return type:

Any

parameter_values()[source]

Return active sweepable port values.

Return type:

dict[str, Any]

set_parameter_value(name, value)[source]

Set one port parameter on an isolated chip copy.

Parameters:
Return type:

None

copy()[source]

Return an independent port retaining label-based targets.

Return type:

Port

physics_notes()[source]

Return the input-output convention owned by this port.

Return type:

list[str]

to_dict()[source]

Serialize port targets and scalar coupling data.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Reconstruct a port from label-based serialized data.

Parameters:

data (dict[str, Any])

Return type:

Port

class quchip.ChipTransform(*args, **kwargs)[source]

Bases: Protocol

Structural protocol for any transformation result that yields a chip.

property chip: Chip
class quchip.EliminationResult(chip, effective_params, validity, notes=<factory>)[source]

Bases: object

Store the result of eliminate().

Parameters:
chip

The reduced chip (eliminated mode removed; effect folded into ordinary surviving devices). The input chip is never mutated.

Type:

Chip

effective_params

Derived quantities per survivor: {label: {"lamb_shift", "purcell_rate", "freq_after", "chi", "kappa"}} (GHz for frequencies, 1/ns for rates). chi is the dispersive pull χ_pull f_mode(survivor in |1⟩) f_mode(survivor in |0⟩) — the full resonator pull per survivor excitation, i.e. the σ_z-convention χ of H_disp = (ω_r + χσ_z)a†a. For a mode touching more than one survivor, the mode is a bus / coupler, not a readout mode, so chi is reported as 0.0 for every survivor. Otherwise chi is a deferred entry: computed from the input chip’s dressed spectrum (one full-chip diagonalization) on the first ["chi"] access and cached, so reductions that never read it stay pure algebra (LazyEffectiveParams); gradients through it follow Chip.dispersive_shift’s backend rule, while every other entry remains backend-independent algebra. kappa is the eliminated mode’s own decay rate, from its intrinsic_decay_rate() (e.g. a resonator’s combined 2π·f_mode/Q photon loss plus 1/T1), or 0.0 when the mode declares no decay channel.

A mode touching two or more survivors additionally emits an "exchange" entry describing the mediated survivor-survivor coupling(s) derived from the Sylvester/exact pair extraction (quchip.chip.sw). For exactly two survivors (the bridge case) this is a single dict: {"j_eff", "dJ_domega_c", "between", "folded_into", "zz", "pathways"}. For three or more survivors, every survivor pair induces its own mediated exchange, so "exchange" is instead a dict keyed by the pair (label_a, label_b), each value the same per-pair schema. In both cases: j_eff is the mediated exchange J = g_a g_b / 2 · (1/Δ_a + 1/Δ_b) (F. Yan et al., Phys. Rev. Applied 10, 054062 (2018)), read off the method="sw"/"exact" pair extraction; dJ_domega_c is its analytic derivative w.r.t. the eliminated mode’s frequency (used by the flux-drive retarget rule); folded_into is the label of the effective edge the exchange landed on (an existing direct coupling between the pair, or a freshly emitted Capacitive or TunableCapacitive); zz is the exact residual ZZ between the pair (method="exact" only — None under "sw", where it is a higher-order correction not represented); pathways is the top virtual-state attribution of the exchange (method="sw" only, from quchip.chip.sw.pathway_attribution()None under "exact", which has no perturbative generator to attribute).

Type:

dict[str, Any]

validity

Per eliminated coupling: {coupling_label: {"g_over_delta", "is_valid", "min_block_gap"}}. min_block_gap is the smallest bare-energy gap the Sylvester generator crossed for this mode (quchip.chip.sw.sylvester_generator()), shared across every coupling touching the mode. When eliminate runs under jax.jit/grad, is_valid (g_over_delta < 0.1) is a traced boolean, not a Python bool — read it outside the traced region, or branch on it with jnp.where rather than if.

Type:

dict[str, Any]

notes

Explicitly dropped physics (counter-rotating, transients, higher order).

Type:

list[str]

Every mapping above is a :class:`~quchip.utils.labeling.LabelKeyedDict`
the device or coupling *object* is as good a key as its label, and pair
keys match in either order
``res.effective_params[q1]``, ``res.validity[leg]``, and
``exchange[(q1, q0)]`` all resolve.
chip: Chip
effective_params: dict[str, Any]
validity: dict[str, Any]
notes: list[str]
describe()[source]

Plain-text fold report: every fold stated explicitly, before -> after.

Per survivor: freq (and T1 when either side carries one), each Lamb-shifted/Purcell-folded value read back from effective_params rather than a stored “before” chip — freq_before = freq_after - lamb_shift and T1_before from purcell_rate are exact identities of how eliminate() derives freq_after/T1, not an approximation. Multi-survivor targets add the emitted exchange edge (Yan-formula tag) and a ZZ line (unavailable under method="sw", the exact residual under method="exact"); any control-line retarget and the per-coupling validity verdict follow. Traced parameters render as <traced> and are never concretized; for use outside jit/grad regions, like every other describe() in the package.

Return type:

str

quchip.eliminate(chip, target, *, method='sw')[source]

Reduce a far-detuned device or an edge coupling, returning a reduced chip.

target is resolved against the chip’s device and coupling namespaces (disjoint by construction — Chip rejects a coupling label that collides with a device label) and dispatches to one of two model reductions:

  • Device target — adiabatic elimination of a far-detuned mode, via quchip.chip.sw. A mode touching one survivor folds into a Lamb shift (and a Purcell channel when the mode dissipates). A mode touching two or more survivors — bus / tunable-coupler (bridge) or several at once — additionally induces a mediated exchange J = g_a g_b / 2 · (1/Δ_a + 1/Δ_b) between every survivor pair (with ∂J/∂ω_c recorded alongside it), folded into the direct coupling between that pair when one exists or added as a new edge otherwise. A fixed eliminated mode emits a Capacitive; a mode that declares frequency control, has a retargeted flux line, or folds into an already-modulable edge emits a TunableCapacitive. At a tunable coupler’s idle point, its g_0 is the net coupling the pair feels, including any direct-edge cancellation. Eliminating several couplers is sequential composition: eliminate(eliminate(chip, "TC1").chip, "TC2").

  • Coupling target — dispersive reduction of an exchange edge to its dressed cross-Kerr shift: both endpoint devices survive (Lamb-shifted), and the coupling itself is replaced by a CrossKerr carrying the dressed pull (see reduce_coupling()). This is the effective-readout-chip flow — reduce a qubit-resonator exchange edge to the diagonal interaction an ordinary charge line probes. method has no effect here: no mode is removed, so the reduction always reads the chip’s exact dressed spectrum.

Parameters:
  • chip (Chip) – Source chip (never mutated).

  • target (Any) – The device or coupling to eliminate — label string or object.

  • method (str) – Device targets only. "sw" (default) is the 2nd-order Schrieffer-Wolff reduction (Bravyi, DiVincenzo & Loss, Ann. Phys. 326, 2793 (2011)) — cheap, differentiable, and what every effective parameter above is derived from perturbatively. "exact" instead exactly diagonalizes the same resolved static model as the SW route (exact-from-dressing, quchip.chip.sw.exact_reduction()) — exact kept-block energies (what residual ZZ needs) at the cost of a full diagonalization, and it raises when near-degenerate dressed states make the bare labeling ambiguous. Any other value raises ValueError.

Return type:

EliminationResult

Examples

>>> from quchip import DuffingTransmon, Resonator, Capacitive, Chip
>>> from quchip.chip.transformations import eliminate
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q")
>>> r = Resonator(freq=7.0, levels=5, label="r")
>>> chip = Chip([q, r], couplings=[Capacitive(q, r, g=0.05)])
>>> result = eliminate(chip, r)          # r removed; Lamb shift folded into q.freq
>>> reduced = result.chip
>>> [d.label for d in reduced.devices]
['q']
class quchip.ActivePatchResult(chip, sequence, active_labels, eliminated_labels, steps, notes)[source]

Bases: object

A reduced patch chip, the re-bound sequence, and the reduction validity record.

Parameters:
chip

The patch chip: schedule-active devices plus whatever spectators active_patch() could not eliminate (unreachable, or where elimination itself declined — see notes).

Type:

Any

sequence

A QuantumSequence bound to chip, replaying the source sequence’s entries verbatim.

Type:

Any

active_labels

Sorted schedule-active device labels (never eliminated).

Type:

tuple[str, …]

eliminated_labels

Device labels actually folded away, in elimination order (farthest-from-active first).

Type:

tuple[str, …]

steps

The EliminationResult from each successful eliminate() call, verbatim and in eliminated_labels order — reshape nothing here; read .validity/.effective_params off the step objects through the convenience properties below.

Type:

tuple

notes

Every explicitly dropped or deferred piece of physics: unreachable spectators left in place, control lines stripped as unused, and (if elimination itself couldn’t proceed past some spectator) why the reduction stopped early — plus every step’s own notes.

Type:

tuple[str, …]

chip: Any
sequence: Any
active_labels: tuple[str, ...]
eliminated_labels: tuple[str, ...]
steps: tuple
notes: tuple[str, ...]
property validity: dict[str, Any]

{eliminated label: that step's .validity} — verbatim, per-coupling shape untouched.

property effective_params: dict[str, Any]

{eliminated label: that step's .effective_params} — verbatim, per-survivor shape untouched.

simulate(**kwargs)[source]

Solve the patch sequence (automatic partitioning still applies inside).

Parameters:

kwargs (Any)

Return type:

Any

quchip.active_patch(sequence, *, hops=1, method='sw')[source]

Reduce a chip to its schedule-active patch by eliminating spectators.

Spectators are eliminated one at a time via eliminate(), farthest-from-active first; every step’s validity metrics ride on the result untouched. Explicit opt-in — this approximates (Schrieffer-Wolff, or the exact dressed-spectrum route under method="exact"), unlike the exact automatic partitioning simulate() performs internally at solve time.

The elimination order and reachability are recomputed from the current reduced chip before every step, not just once up front: a fold can only ever add a bridging edge between the eliminated mode’s neighbors, never remove one, so a label’s distance from the active set can shorten but never make a previously reachable label unreachable — still, reading the graph fresh each time means a label that has already been folded away is never re-considered, and the order always reflects what eliminate will actually see. Bridging edges created by earlier folds may be Capacitive edges carrying g or TunableCapacitive edges carrying g_0. Both fold onward, so cycles among spectators reduce all the way down. If eliminate still declines a step (its typed NotImplementedError signal for an unsupported elimination — e.g. a Purcell decay fold onto a survivor that carries thermal occupation, which no collapse-channel API can represent without inventing physics that was never present), the reduction stops there: everything eliminated so far stays folded, and the remaining spectators — including the one that failed — are left on the patch chip untouched, with the reason recorded in notes.

Parameters:
  • sequence (QuantumSequence) – The schedule to reduce around.

  • hops (int) – Coupling-graph hops the active set expands beyond the scheduled targets (see active_labels()).

  • method (str) – Forwarded to eliminate() for every device elimination.

Return type:

ActivePatchResult

quchip.register_retarget_rule(drive_type, target_type, result_kind, rule)[source]

Register a converter for (drive type, eliminated-target type, result kind).

Lookup (lookup_retarget_rule()) walks both types’ MROs, so a rule registered for a base type also covers its subclasses; result_kind matches exactly. This is the extension point for teaching eliminate() to carry a new kind of stranded control line without modifying it.

Parameters:
  • drive_type (type) – Control-line class the rule handles.

  • target_type (type) – Eliminated-target class the rule handles: a device type (the eliminated mode itself) for "edge"/"leaf-fold".

  • result_kind (str) – "edge", "leaf-fold", or "crosskerr".

  • rule (callable) – rule(line, ctx: RetargetContext) -> RetargetResult.

Return type:

None

quchip.register_elimination_target(target)[source]

Register an EliminationTarget kind for eliminate() to dispatch on.

Parameters:

target (EliminationTarget)

Return type:

None

quchip.register_reduction_method(method)[source]

Register a reduction strategy under its ReductionMethod.name.

Parameters:

method (ReductionMethod)

Return type:

None

class quchip.Gaussian(duration, sigmas=3, amplitude=1.0)[source]

Bases: Envelope

Centered Gaussian pulse.

\[E(t) = A \exp\!\left[-\frac{(t - \tau/2)^2}{2 \sigma^2}\right], \qquad \sigma = \frac{\tau}{2 N_\sigma}.\]

The sigmas parameter \(N_\sigma\) is the number of standard deviations from the pulse center to its edge at t = 0 or t = duration. Gaussian pulses minimize spectral leakage onto higher transmon levels and are the starting point for DRAG corrections (Motzoi et al., PRL 103, 110501 (2009)).

The scheduled window [0, duration] starts and ends at amplitude * exp(-sigmas**2 / 2), not zero — about 0.011 * amplitude at the default sigmas=3. The pulse turns on and off with that jump; the Gaussian waveform itself is unchanged.

Parameters:
  • duration (Any)

  • sigmas (Any)

  • amplitude (Any)

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Evaluate the centered Gaussian envelope at time points t.

Parameters:

t (Any)

Return type:

Any

class quchip.GaussianDRAG(duration, sigmas=3, amplitude=1.0, beta=0.0)[source]

Bases: Envelope

Gaussian pulse with a derivative quadrature.

\[E(t) = I(t) + i\,\beta\,\frac{dI}{dt}, \qquad I(t) = A\exp\!\left[-\frac{(t-\tau/2)^2}{2\sigma^2}\right].\]

beta is signed and measured in ns. Its sign therefore owns the quadrature convention without an additional polarity flag.

Parameters:
duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
beta: Any = Parameter(default=0.0, positive=False, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Return complex I/Q shape at time relative to the pulse start.

Parameters:

t (Any)

Return type:

Any

class quchip.LinearRamp(duration, ramp_duration, amplitude=1.0)[source]

Bases: Envelope

Linearly rising ramp that holds at peak amplitude.

The envelope rises linearly from 0 to amplitude over the first ramp_duration nanoseconds, then holds constant at amplitude for the remainder of the pulse.

\[E(t) = A \cdot \min\!\left(\frac{t}{\tau_r},\, 1\right), \qquad 0 \le t \le \tau,\]

where \(\tau_r\) is ramp_duration and \(\tau\) is duration.

Parameters:
  • duration (float) – Total pulse duration in ns. Must be > 0.

  • ramp_duration (float) – Duration of the linear rise in ns. Must satisfy 0 < ramp_duration <= duration.

  • amplitude (float) – Peak amplitude \(A\) (default 1.0).

Notes

For an adiabatic ramp into a Kerr-cat qubit, choose ramp_duration long compared to 1 / (2 * K) (the inverse gap at the bifurcation point). See Grimm et al., Nature 584, 205 (2020).

The waveform is JAX-traceable: ramp_duration and amplitude may be JAX tracers so the ramp parameters are differentiable.

Examples

>>> from quchip.control.envelopes import LinearRamp
>>> ramp = LinearRamp(duration=60.0, ramp_duration=50.0, amplitude=4.0)
>>> import numpy as np
>>> t = np.array([0.0, 25.0, 50.0, 55.0])
>>> np.real(ramp.value(t)).tolist()
[0.0, 2.0, 4.0, 4.0]
duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
ramp_duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject ramps longer than the pulse (ramp_duration > duration).

Return type:

None

value(t)[source]

Evaluate the linear-ramp envelope at time points t (ns).

Parameters:

t (array-like) – 1-D array of time points in nanoseconds.

Returns:

Complex-valued waveform: rises linearly over ramp_duration, holds constant at amplitude afterward.

Return type:

array

class quchip.GaussianEdge(duration, edge_duration, sigmas=3, amplitude=1.0)[source]

Bases: Envelope

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

Each edge is a Gaussian of width \(\sigma = \tau_e / (2 N_\sigma)\) where \(\tau_e\) = edge_duration; the plateau between edges holds a constant amplitude \(A\). Total duration includes both edges. Commonly used for two-qubit gates (Krantz et al. 2019, Sec. IV.C) because the flat top sets the gate area while the Gaussian edges suppress spectral leakage.

Parameters:
  • duration (float) – Total pulse length, including both edges, in ns.

  • edge_duration (float) – Ramp time \(\tau_e\) per edge, in ns. Must satisfy 2 * edge_duration <= duration.

  • sigmas (float) – Number of standard deviations spanned by each edge.

  • amplitude (float) – Plateau amplitude \(A\).

See also

SquareWithGaussianEdges

Same shape parameterized by edge_frac (fraction) instead of absolute edge_duration.

References

  • Krantz et al., APR 6, 021318 (2019), Sec. IV.C.

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
edge_duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject edges that overrun the pulse (2 * edge_duration > duration).

Return type:

None

value(t)[source]

Evaluate the flat-top Gaussian-edge envelope at time points t.

Parameters:

t (Any)

Return type:

Any

class quchip.Square(duration, amplitude=1.0)[source]

Bases: Envelope

Constant-amplitude pulse.

\[E(t) = A, \qquad 0 \le t \le \tau.\]
Parameters:
  • duration (float) – Pulse length \(\tau\) in ns.

  • amplitude (float) – Real amplitude \(A\) applied on top of value().

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
value(t)[source]

Evaluate the constant envelope at time points t.

Parameters:

t (Any)

Return type:

Any

class quchip.SquareWithGaussianEdges(duration, amplitude=1.0, edge_frac=0.25, sigmas=3)[source]

Bases: Envelope

Flat-top pulse with Gaussian ramp-up and ramp-down edges.

Each ramp has duration \(\tau_e = f_e \cdot \tau\) with \(f_e\) = edge_frac; the plateau between ramps holds amplitude \(A\). Total duration includes both edges. The Gaussian width is \(\sigma = \tau_e / (2 N_\sigma)\) with \(N_\sigma\) = sigmas.

This is the canonical shape used in Krantz et al. 2019 (Sec. IV.C) for two-qubit gates — the flat top sets the gate area while the Gaussian edges suppress spectral leakage. Parametrizing the ramp as a fraction of the total duration makes the shape shape-invariant under changes of duration.

Parameters:
  • duration (float) – Total pulse length in ns (includes both ramps).

  • amplitude (float) – Plateau amplitude \(A\).

  • edge_frac (float) – Ramp length as a fraction of the total duration. Must satisfy 0 < edge_frac and 2 * edge_frac <= 1.

  • sigmas (float) – Number of standard deviations spanned by each ramp.

duration: Any = Parameter(default=unbound, positive=True, nonnegative=False, serialize=True, unit='ns', symbol=None, noise=False, kw_only=False, required=True)
amplitude: Any = Parameter(default=1.0, positive=False, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
edge_frac: Any = Parameter(default=0.25, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
sigmas: Any = Parameter(default=3, positive=True, nonnegative=False, serialize=True, unit=None, symbol=None, noise=False, kw_only=False, required=False)
validate()[source]

Reject ramps that overrun the pulse (2 * edge_frac > 1).

Return type:

None

property edge_duration: float

Ramp duration in ns (edge_frac * duration).

value(t)[source]

Evaluate the fraction-parameterized Gaussian-edge envelope.

Parameters:

t (Any)

Return type:

Any

quchip.simulate(chip, drive_ops, tlist, *, solver=None, options=None, e_ops=None, initial_state=None, check_truncation=True, truncation_threshold=0.001, partition=True, approximation=None)[source]

Build a SolveProblem, dispatch it, and wrap the solver output.

Parameters mirror build_problem(). solver is "sesolve" or "mesolve"; None auto-selects mesolve when collapse operators exist. e_ops is dict-form, keyed by device label (or a 2-tuple of labels for two-body observables), and favors object references via resolve_label(). The Hilbert-truncation safety net is inherited from solve_problem(); check_truncation / truncation_threshold are threaded down.

Parameters:
  • chip (Chip) – The chip to simulate.

  • drive_ops (list of DriveOp) – Scheduled drive operations, typically produced by a QuantumSequence.

  • tlist (array_like) – Solver time grid in ns.

  • solver ({"sesolve", "mesolve"}, optional) – Solver selection; None auto-selects mesolve when collapse operators are present, else sesolve.

  • options (dict, optional) – Backend solver options. Must not contain a "backend" key (backend selection is chip-owned).

  • e_ops (dict, optional) – Observables keyed by device label (or a 2-tuple of labels for a two-body observable).

  • initial_state (optional) – Initial state. None defaults to the chip ground state. A Mapping (device label/object -> energy level, e.g. {"q0": 1}) becomes a product state in the engine’s resolved local bases on both the joint and partitioned paths. An authored full-space ket is projected into that same solver space.

  • check_truncation (bool, default True) – Screen the result for over-populated top Fock levels.

  • truncation_threshold (float, default 1e-3) – Top-level population above which the truncation check warns.

  • partition (bool, default True) – When the chip splits into independent sub-chips (see Chip.partition()), dispatch one solve per component and combine them into a PartitionedSimulationResult instead of solving the full tensor-product space. Declines back to the joint solve (returning a plain SimulationResult) when the partition is trivial or initial_state is a raw backend state rather than None/a Mapping. Set False to force the joint solve unconditionally. simulate_batch/solve_many always solve the full chip without partitioning.

  • approximation (Any | None)

Returns:

The wrapped solver output.

Return type:

SimulationResult or PartitionedSimulationResult

Raises:
  • ValueError – If solver is neither "sesolve" nor "mesolve", if tlist is not one-dimensional, finite, strictly increasing, and at least two points long, or if any drive_ops entry’s pulse window does not overlap tlist with positive measure (both concrete-only checks; see build_problem()).

  • RuntimeError – If the backend solve fails.

Examples

>>> import numpy as np
>>> from quchip import RWA, Chip, DuffingTransmon, ChargeDrive, Gaussian, QuantumSequence
>>> from quchip.engine import simulate
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.3, levels=3)
>>> chip = Chip([q], frame="rotating", approximation=RWA())
>>> ctrl = ChargeDrive(target=q)
>>> chip.wire(ctrl)
>>> seq = QuantumSequence(chip)
>>> _ = seq.schedule(ctrl, envelope=Gaussian(duration=20.0, sigmas=3, amplitude=0.02), freq=chip.freq(q))
>>> tlist = np.linspace(0.0, 20.0, 41)
>>> result = simulate(chip, list(seq.scheduled_ops), tlist, e_ops={q: q.number_operator()})
>>> populations = result.expect(q)
quchip.build_problem(chip, drive_ops, tlist, *, solver=None, options=None, e_ops=None, initial_state=None, approximation=None)[source]

Resolve, assemble, and package a frozen SolveProblem.

Returns an immutable request that can be passed to solve_problem(), batched with solve_many(), or serialized. No solver is invoked.

Parameters:
  • chip (Chip) – The chip whose Hamiltonian, frame, and backend are assembled.

  • drive_ops (list of DriveOp) – Scheduled drive operations, typically produced by a QuantumSequence.

  • tlist (array_like) – Solver time grid in ns.

  • solver ({"sesolve", "mesolve"}, optional) – Solver selection; None auto-selects mesolve when collapse operators are present, else sesolve.

  • options (dict, optional) – Backend solver options. Must not contain a "backend" key (backend selection is chip-owned).

  • e_ops (dict, optional) – Observables keyed by device label (or a 2-tuple of labels for a two-body observable).

  • initial_state (optional) – Initial state; None defaults to the chip ground state.

  • approximation (Any | None)

Returns:

The frozen request handed to a backend.

Return type:

SolveProblem

Raises:

ValueError – If tlist is not one-dimensional, finite, strictly increasing, and at least two points long, or if any drive_ops entry’s pulse window [start_time, start_time + envelope.duration] does not overlap tlist with positive measure. Both checks are concrete-only and skip silently under JAX tracing.

Examples

>>> import numpy as np
>>> from quchip import RWA, Chip, DuffingTransmon, ChargeDrive, Gaussian, QuantumSequence
>>> from quchip.engine import build_problem, solve_problem
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.3, levels=3)
>>> chip = Chip([q], frame="rotating", approximation=RWA())
>>> ctrl = ChargeDrive(target=q)
>>> chip.wire(ctrl)
>>> seq = QuantumSequence(chip)
>>> _ = seq.schedule(ctrl, envelope=Gaussian(duration=20.0, sigmas=3, amplitude=0.02), freq=chip.freq(q))
>>> problem = build_problem(chip, list(seq.scheduled_ops), np.linspace(0.0, 20.0, 41))
>>> result = solve_problem(problem)
quchip.solve_problem(problem, *, check_truncation=True, truncation_threshold=0.001)[source]

Dispatch a SolveProblem through its chip backend.

All single-solve paths call this function. Unless check_truncation=False, it screens the wrapped result for over-populated top Fock levels and warns above truncation_threshold.

Parameters:
Return type:

SimulationResult

quchip.solve_many(batch_or_problems, *, progress=True)[source]

Batch-dispatch typed solve requests that share one chip configuration.

Accepts a SolveBatch or a flat list of SolveProblem objects. The batched path is preferred: backends convert shared operators exactly once and stitch per-element coefficients into one parallel solve.

Parameters:
Return type:

SimulationBatchResult

quchip.steadystate(chip, **kwargs)[source]

Solve a chip’s unique static Lindblad steady state.

Parameters:
Return type:

Any

quchip.steadystate_batch(chip, *axes, **kwargs)[source]

Solve static Lindblad steady states over parameter axes.

Parameters:
Return type:

Any

quchip.build_steadystate_problem(chip, **kwargs)[source]

Build a frozen static Lindblad steady-state request.

Parameters:
Return type:

SteadyStateProblem

class quchip.ObservableTrace(values, raw)[source]

Bases: object

One named expectation-value trace, with pre- and post-processing values.

Parameters:
values

Post-processed expectation values over time (e.g. demodulated, phase-corrected, band-summed). This is what user code normally wants.

Type:

Any

raw

The same quantity before post-processing — useful for debugging frame conventions, band decomposition, and demodulation.

Type:

Any

values: Any
raw: Any
class quchip.SimulationBatchResult(results, *, shape=None, axes=None)[source]

Bases: object

Ordered, immutable batch of SimulationResult with stacked helpers.

Returned by solve_many() and by any sweep that solves many problems in one call. The batch preserves iteration order so that per-element results map one-to-one onto the inputs that produced them.

The final_* helpers stack along a new leading batch axis in the backend’s array module, and the grid-aware expect() / population() accept reduce='last' for a final-value slice, so a loss function that sums over the batch stays JAX-traceable end-to-end.

Parameters:
property results: tuple[SimulationResult, ...]

Return the per-element SimulationResult objects in input order.

property shape: tuple[int, ...]

Return the natural sweep-grid shape for this batch.

property axes: tuple[tuple[str, Any], ...]

Return sweep-axis metadata as (name, values) pairs.

property backend: Backend

Return the backend shared by every element (raises on an empty batch).

with_sweep_metadata(*, shape, axes)[source]

Return an equivalent batch annotated with sweep-axis metadata.

Parameters:
Return type:

SimulationBatchResult

expect(key, index=None, *, reduce=None)[source]

Return expectation traces reshaped to the natural sweep grid.

Parameters:
  • key (Any)

  • index (int | None)

  • reduce (str | None)

Return type:

Any

population(device, level=0, *, reduce=None)[source]

Return population traces reshaped to the natural sweep grid.

Parameters:
Return type:

Any

final_overlap_magnitudes(targets)[source]

Return stacked final overlap magnitudes, one per (result, target) pair.

Parameters:

targets (list[Any] | tuple[Any, ...])

Return type:

Any

final_amplitudes(targets)[source]

Return stacked final complex amplitudes (phase-sensitive), one per (result, target) pair.

Parameters:

targets (list[Any] | tuple[Any, ...])

Return type:

Any

class quchip.SimulationResult(solver_result, backend, dims, *, device_info=None, observable_traces=None)[source]

Bases: object

Backend-agnostic container for the output of one solve.

A SimulationResult bundles everything a user needs after a solve finishes:

  • times — the time grid the solver stored (ns).

  • states — the list of stored states (kets or density matrices), or None if store_states was off.

  • solver — the name reported by the backend.

  • stats — a plain dict of solver statistics.

  • dims — the per-device Hilbert-space dimensions, in chip order.

  • device_info[(label, computational), …] for partial-trace helpers to resolve device indices by label or by object.

Expectation values live in observable_traces when e_ops was passed as a dict. Each entry is an ObservableTrace or a list of them (list-valued observables, one per band, etc.).

Parameters:
property observable_traces: dict[Any, ObservableTrace | list[ObservableTrace]] | None

Return the dict of named ObservableTrace entries, or None.

None when e_ops was not passed as a dict. Visualization and analysis code that wants the full dict should read this property rather than the private _expect_data attribute.

expect(key, index=None)[source]

Return the full expectation-value array for observable key over self.times.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_final(key, index=None)[source]

Return the final expectation value for observable key (self.expect(key)[-1]).

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_values(key, index=None)

Return the full expectation-value array for observable key over self.times.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

overlap_array(target)[source]

Return the overlap with target at every stored time.

For ket trajectories returns |<target|psi(t)>|**2; for density matrices returns <target|rho(t)|target>. Stays in the backend’s array module so the result is differentiable. One batched op over the leading time axis — no per-point loop.

Parameters:

target (Any)

Return type:

Any

amplitude_array(target)[source]

Return the phase-sensitive complex projection <target|psi(t)> for kets.

Density-matrix trajectories raise TypeError — there is no single phase-sensitive amplitude for a mixed state; use overlap_array() instead. One batched op, no per-point loop.

Parameters:

target (Any)

Return type:

Any

overlap(target)[source]

Wrap overlap_array() for convenience.

Returns a NumPy array under the QuTiP backend; under JAX (dynamiqs) returns the backend-native array so the call stays JIT/grad-friendly.

Parameters:

target (Any)

Return type:

Any

state(t=None, *, dm=False)[source]

Return the state at time t (or final state if t is None); optionally coerced to a DM.

Parameters:
Return type:

Any

state_at(t)[source]

Return the state at the stored time nearest to t (ns).

Parameters:

t (float)

Return type:

Any

dm_at(t)[source]

Return the density matrix at the stored time nearest to t (ns) — promotes kets on demand.

Parameters:

t (float)

Return type:

Any

property final_state: Any

Return the final state — explicit final_state if stored, else the last stored trajectory entry.

reduced_state(t, device)[source]

Partial-trace the state at time t down to device’s subspace.

Parameters:
Return type:

Any

property populations: dict[tuple[int, ...], ndarray]

Return per-basis-state populations |<n1, n2, ...|psi(t)>|**2 over time.

Returns a dict keyed by Fock tuple (n1, n2, ..., nK) — one per computational basis vector of the full chip — mapping to a real numpy.ndarray of length len(self.times).

Requires store_states; density-matrix trajectories are handled transparently by reading the diagonal of each timestep’s DM.

population_array(device, level=0)[source]

Return the population of Fock level on device over time, in the backend’s array module.

Parameters:
Return type:

Any

population(device, level=0)[source]

Wrap population_array() for convenience.

Returns a NumPy array under the QuTiP backend; under JAX (dynamiqs) returns the backend-native array so the call stays JIT/grad-friendly. Use population_array() directly when you want to keep gradient flow regardless of context.

Parameters:
Return type:

Any

plot_populations(*, trace_out=None, computational=False, ax=None, **kwargs)[source]

Plot per-basis-state populations over time (delegates to quchip.viz.plot_populations()).

Parameters:
Return type:

Any

plot_state(index, *, trace_out=None, computational=False, mode='population', ax=None, **kwargs)[source]

Plot one stored state as populations or a density matrix (delegates to quchip.viz.plot_state()).

Parameters:
Return type:

Any

plot_expectation(*, keys=None, ax=None, **kwargs)[source]

Plot expectation-value traces over time (delegates to quchip.viz.plot_expectation()).

Parameters:
Return type:

Any

plot_wigner(index=-1, *, trace_out=None, ax=None, **kwargs)[source]

Plot the Wigner function of one stored state (delegates to quchip.viz.plot_wigner()).

Parameters:
Return type:

Any

check_truncation(*, threshold=0.001, top_levels=1)[source]

Emit a UserWarning per device whose top-top_levels population exceeds threshold.

Uses the final stored state only — cheap, one full-chip diagonal read, no per-timestep loop, no partial traces. Silently no-ops when no final state is available (e.g. store_states and store_final_state both disabled).

Returns the per-device top-level population actually observed, keyed by device label, so callers can surface the numbers without re-parsing the warning text.

Parameters:
Return type:

dict[str, float]

class quchip.SteadyStateResult(state, residual, trace, trace_error, hermiticity_error, minimum_eigenvalue, positivity_error, nullity, condition_number, dims, device_info, stats, _backend, _expectations)[source]

Bases: object

One normalized stationary density matrix and its diagnostics.

Parameters:
state: Any
residual: Any
trace: Any
trace_error: Any
hermiticity_error: Any
minimum_eigenvalue: Any
positivity_error: Any
nullity: Any
condition_number: Any
dims: tuple[int, ...]
device_info: tuple[tuple[str, bool], ...]
stats: Mapping[str, Any]
property is_unique: Any

Return uniqueness, or None when the backend skipped that diagnostic.

expect(key, index=None)[source]

Return one named stationary expectation value.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

reduced_state(device)[source]

Partial-trace the stationary state down to one device.

Parameters:

device (str | BaseDevice)

Return type:

Any

class quchip.SteadyStateBatchResult(results, *, shape, axes)[source]

Bases: object

Immutable stationary results reshaped to their declared sweep grid.

Parameters:
property results: tuple[SteadyStateResult, ...]

Return stationary results in C-order sweep order.

property shape: tuple[int, ...]

Return the natural sweep-grid shape.

property axes: tuple[tuple[str, Any], ...]

Return named sweep-axis metadata.

property backend: Backend

Return the backend shared by every result.

expect(key, index=None)[source]

Return one expectation value reshaped to the sweep grid.

Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

class quchip.SParameterResult(frequencies, input_port, output_ports, input_amplitudes, input_photon_fluxes, input_powers, axes, shape, steady_states, diagnostics, _response)[source]

Bases: object

Complex port response over a declared VNA sweep grid.

Parameters:
frequencies: Any
input_port: str
output_ports: tuple[str, ...]
input_amplitudes: Any | None
input_photon_fluxes: Any | None
input_powers: Any | None
axes: tuple[tuple[str, Any], ...]
shape: tuple[int, ...]
steady_states: tuple[Any, ...]
diagnostics: tuple[Mapping[str, Any], ...]
property input_power_unit: str

Unit of input_powers.

property axis_names: tuple[str, ...]

Names of the result axes, in array order.

s(output, input=None)[source]

Return one complex S(output, input) array.

Parameters:
  • output (Any)

  • input (Any | None)

Return type:

Any

property s11: Any

Reflection at the swept input port.

property s21: Any

Transmission to the first requested output distinct from the input.

class quchip.OutputSpectrumResult(port, frequencies, fluctuation_spectrum, output_photon_flux, coherent_flux, incoherent_flux, steady_state, fourier_convention='2 Re integral_0^inf d tau exp(+i 2 pi f tau) C(tau)')[source]

Bases: object

Normally ordered stationary output-field fluctuation spectrum.

Parameters:
  • port (str)

  • frequencies (Any)

  • fluctuation_spectrum (Any)

  • output_photon_flux (Any)

  • coherent_flux (Any)

  • incoherent_flux (Any)

  • steady_state (Any)

  • fourier_convention (str)

port: str
frequencies: Any
fluctuation_spectrum: Any
output_photon_flux: Any
coherent_flux: Any
incoherent_flux: Any
steady_state: Any
fourier_convention: str = '2 Re integral_0^inf d tau exp(+i 2 pi f tau) C(tau)'
property total_flux: Any

Mean normally ordered output photon flux.

class quchip.OutputCorrelationResult(order, input_port, output_port, delays, values, unnormalized, input_intensity, output_intensity, steady_state, normalization)[source]

Bases: object

Normalized stationary output-field correlation versus delay.

Parameters:
  • order (int)

  • input_port (str)

  • output_port (str)

  • delays (Any)

  • values (Any)

  • unnormalized (Any)

  • input_intensity (Any)

  • output_intensity (Any)

  • steady_state (Any)

  • normalization (str)

order: int
input_port: str
output_port: str
delays: Any
values: Any
unnormalized: Any
input_intensity: Any
output_intensity: Any
steady_state: Any
normalization: str
property port: str

Delayed output port, retained for single-port result code.

property intensity: Any

Delayed output intensity, retained for single-port result code.

class quchip.PartitionedSimulationResult(component_results, partition, key_plan)[source]

Bases: object

Result of one partitioned solve: K component results + a key plan.

Parameters:
  • component_results (list)

  • partition (Any)

  • key_plan (dict)

property components: tuple
property partition: Any
property device_order: tuple[str, ...]

Return the parent chip’s original device-label order.

This is not the concatenation of each component’s labels (which follows connected-component discovery order and can interleave differently whenever the chip’s device order doesn’t already group each component’s members together) — it is the order states and final_state are permuted into so they match the joint solve exactly.

expect(key, index=None)[source]
Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_final(key, index=None)[source]
Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

expect_values(key, index=None)
Parameters:
  • key (Any)

  • index (int | None)

Return type:

Any

population(device, level=0)[source]
Parameters:
Return type:

Any

population_array(device, level=0)[source]
Parameters:
Return type:

Any

check_truncation(threshold=0.001)[source]

Run each component’s truncation check and merge the per-device results.

Mirrors check_truncation()’s return shape (a dict keyed by device label) for duck-typing parity between a joint and a partitioned result.

Parameters:

threshold (float)

Return type:

dict[str, float]

property states: list

Reconstruct the joint-state trajectory (ket trajectories only), in device_order.

Component states tensor together in connected-component discovery order, which can interleave differently from the parent chip’s own device order; each reconstructed step is permuted (permute_state()) into device_order so the result matches a joint solve of the original chip exactly.

See also final_state, which — unlike this accessor — intentionally also accepts density-matrix components: a tensor product of component density matrices is itself a valid joint state, whereas a per-step list of joint kets is only well-defined when every component stayed pure.

property final_state: Any

Reconstruct the joint final state, in device_order — a ket if every component stayed pure, otherwise a density matrix.

When components disagree, every component is first promoted to a density matrix (_promote_to_common_state_kind()) before tensoring, giving a valid joint density matrix. Components tensor together in connected-component discovery order, which can interleave differently from the parent chip’s own device order; the result is permuted (permute_state()) into device_order so it matches a joint solve of the original chip exactly.

describe()[source]
Return type:

str

class quchip.QuantumSequence(chip)[source]

Bases: object

Declarative pulse sequence builder for a Chip.

Tracks per-(device, drive) timing cursors and per-device virtual-Z phase frames. Append pulses with schedule() (or the conveniences charge(), phase(), flux()); synchronize channels with barrier() and delay(). The schedule is materialized lazily into DriveOp objects by build_problem() and build_batch().

Wherever a device/drive is expected, either the object itself or its string label works, and examples should prefer object references.

Simulation runs through one consistent verb — simulate — across three tiers, from most ergonomic to most explicit:

  1. simulate() / simulate_batch() — schedule and solve in one call (the example-facing path).

  2. solve() / solve_many() — solve a SolveProblem / batch you already hold.

  3. The module-level simulate() / solve_problem() / solve_many() — the low-level “I already have drive_ops / a SolveProblem” tier.

Parameters:

chip (Chip) – Chip this sequence schedules against. Supplies the device and coupling maps used to resolve scheduling targets, the wired ControlEquipment lines, and the frame/backend settings used by build_problem() and simulate().

Examples

>>> from quchip import (
...     DuffingTransmon, ChargeDrive, Chip, QuantumSequence, Gaussian
... )
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3)
>>> _ = ChargeDrive(target=q)
>>> chip = Chip([q])
>>> seq = QuantumSequence(chip)
>>> _ = seq.charge(q, envelope=Gaussian(duration=20.0, amplitude=0.05))
>>> seq.vz(q, angle=0.5)
>>> _ = seq.charge(q, envelope=Gaussian(duration=10.0, amplitude=0.03))
schedule(target, *, envelope, freq=None, start_time=None, phase=0.0)[source]

Schedule a pulse on target.

Parameters:
  • target (str | BaseDrive | BaseDevice | BaseCoupling) –

    Accepted forms, resolved in this order:

    • BaseDrive — scheduled directly on that drive.

    • BaseDevice — uses the device’s first connected drive; pass the drive object explicitly when a device has multiple drives.

    • BaseCoupling — uses the unique CouplingDrive targeting that coupling; pass the drive object explicitly when a coupling has multiple control lines.

    • str — a label, resolved in order: a device label; then, only when absent from the device map, a coupling label (the two label spaces are disjoint); then, only when absent from both, a control-equipment line label, scheduling directly on that line. This third fallback is what lets a caller schedule by a drive’s own label after its device or coupling target has been eliminated — see eliminate()’s retarget registry, which preserves a converted line’s label. A control-line label that collides with a device/coupling label is shadowed by the device/coupling resolution.

  • envelope (Envelope) – Pulse envelope.

  • freq (float, optional) – Optional carrier frequency in GHz. Omitting it leaves the signal carrier-free. Drive-specific conveniences may provide their own explicit default, such as charge() using device.drive_freq.

  • start_time (float, optional) – Pulse start time in ns. Defaults to the current cursor; earlier times are rejected.

  • phase (float) – Per-pulse phase offset, composed with any accumulated virtual-Z.

Return type:

PulseHandle

charge(target, *, envelope, freq=None, phase=0.0)[source]

Schedule a ChargeDrive pulse; freq defaults to device.drive_freq.

Parameters:
Return type:

PulseHandle

phase(target, *, envelope, freq, phase=0.0)[source]

Schedule a PhaseDrive pulse; freq is required.

Parameters:
Return type:

PulseHandle

flux(target, *, envelope)[source]

Schedule a FluxDrive pulse (no carrier frequency).

Parameters:
Return type:

PulseHandle

pump(coupling, *, envelope, freq=None, start_time=None, phase=0.0)[source]

Schedule an edge pump: baseband δ(t)=A(t) when freq is None, else A(t)·cos(2πft-φ).

Parameters:
Return type:

PulseHandle

flux_to(target, *, target_freq, envelope)[source]

Schedule a flux-drive frequency-shift pulse to target_freq.

This is not an inverse-SQUID flux calibration. It computes δω = target_freq chip.freq(device) and schedules a FluxDrive pulse whose envelope amplitude is that frequency shift in GHz. The resulting Hamiltonian contribution is the linear detuning term δω(t) . envelope should be passed with amplitude=None (or any placeholder) — this method replaces the amplitude with the computed δω.

target_freq may be a JAX tracer (e.g. chip.freq(other_device)).

Parameters:
  • target (str | BaseDevice) – Device to flux-pulse.

  • target_freq (float) – Target 0 1 frequency in GHz.

  • envelope (Envelope) – Envelope template with all timing parameters set. Its amplitude attribute is replaced by the computed δω; any value passed as amplitude is ignored.

Returns:

Handle to the scheduled entry, usable with .vary().

Return type:

PulseHandle

vz(target, angle)[source]

Apply a virtual-Z frame shift of angle rad on target.

The shift is free (no pulse is emitted) and accumulates into every subsequent microwave pulse on the device via its phase_offset. This is the standard software-Z trick for transmons (McKay et al., PRA 96, 022330 (2017)). Baseband flux pulses are unaffected.

Parameters:
Return type:

None

delay(scope, duration)[source]

Insert an idle delay on all drive channels of scope.

Parameters:
Return type:

DelayHandle

barrier(*labels)[source]

Synchronize channel cursors.

With no arguments, every (device, drive) cursor on the chip is advanced to the current maximum. With explicit device targets, only those devices’ cursors are aligned. Use this to guarantee that pulses scheduled after the barrier start no earlier than any pulse scheduled before it.

Parameters:

labels (str | BaseDevice)

Return type:

None

property total_duration: Any

Total sequence duration in ns (maximum across all cursors).

property scheduled_ops: tuple[DriveOp, ...]

Materialize and return the scheduled DriveOp tuple.

property channel_cursors: dict[tuple[str, str], Any]

Current time cursors keyed by (target_label, drive_label).

build_problem(tlist=None, solver=None, options=None, e_ops=None, initial_state=None, approximation=None)[source]

Build a single SolveProblem from this sequence.

Parameters:
  • tlist (array-like, optional) – Save/output time grid in ns. Defaults to the grid built by _resolve_tlist() from total_duration (10 points/ns, 100-point floor).

  • solver (str, optional) – Backend solver name. Defaults to the backend’s own default solver when omitted.

  • options (dict, optional) – Solver options, merged on top of the defaults {"store_states": True, "store_final_state": True}.

  • e_ops (dict, optional) – Expectation operators keyed by device label (or a 2-tuple of device labels for a two-body operator), mapping to a local operator (or a pair of local operators). Decomposed into per-band terms before reaching the solver.

  • initial_state (Any, optional) – A ket, density matrix, or mapping for state(). Defaults to the chip’s default initial state when omitted.

  • approximation (Approximation | None)

Returns:

Frozen problem — chip, compiled engine result, initial state, tlist, collapse operators, and expectation operators — ready for solve() or solve_problem().

Return type:

SolveProblem

resolve(*, frame=None, approximation=None)[source]

Resolve the backend-neutral Hamiltonian and noise description.

Parameters:
Return type:

EngineResult

hamiltonian()[source]

Return this sequence’s canonical time-dependent Hamiltonian.

Return type:

PhysicsExpr

vary(field, values, *, name=None)[source]

Create a BatchAxis over a public parameter path or state.

Parameters:
  • field (str) – "initial_state" or any dotted path exposed by parameters. Pulse handles remain the concise way to vary a pulse field immediately after scheduling it.

  • values (array-like) – Sequence of values for field, one per batch point.

  • name (str, optional) – Axis name recorded in SolveBatch.axes and SolveBatch.params_at(). Defaults to field.

Returns:

Sequence-level axis, consumable by build_batch() or zip().

Return type:

BatchAxis

zip(*axes)[source]

Zip axes into one pairwise dimension.

Parameters:

*axes (BatchAxis) – Two or more axes to zip, each created by this sequence (vary(), PulseHandle.vary(), or DelayHandle.vary()). Every axis must have the same length.

Returns:

Combined axis that build_batch() treats as a single dimension: point i supplies point i from every zipped axis simultaneously, rather than the outer product.

Return type:

ZippedBatchAxis

build_batch(*axes, tlist=None, solver=None, options=None, e_ops=None, initial_state=None, approximation=None)[source]

Build a batched solve request from explicit sweep axes.

Parameters:
Return type:

SolveBatch

simulate(tlist=None, solver=None, options=None, e_ops=None, initial_state=None, *, backend=None, check_truncation=True, truncation_threshold=0.001, partition=True, approximation=None)[source]

Build and solve a single simulation, routed through simulate().

backend — name ("qutip"/"dynamiqs") or instance — scopes this one call, outranking the chip’s and the process default (so a gradient solve can run on dynamiqs while everything around it stays on QuTiP). Foreign-native initial states are coerced at the solve boundary. Caveat: Python evaluates arguments before the scope opens, so an initial_state=chip.state(...) expression inline in the call is built under the surrounding backend — fine for concrete chips (the state is coerced), but a chip carrying JAX tracers needs a JAX-capable surrounding backend (chip-level or process default) for that construction itself.

Inherits the Hilbert-truncation safety net from solve_problem(); pass check_truncation=False to opt out or retune truncation_threshold.

partition, default True, forwards to simulate(): when the chip splits into independent sub-chips (Chip.partition()), each component is dispatched separately and combined into a PartitionedSimulationResult. This only engages when initial_state is None or a Mapping. String shorthand and concrete states always take the joint path. Pass partition=False to force the joint solve unconditionally.

Parameters:
  • tlist (Any | None)

  • solver (str | None)

  • options (dict | None)

  • e_ops (dict | None)

  • initial_state (Any | None)

  • backend (Any | None)

  • check_truncation (bool)

  • truncation_threshold (float)

  • partition (bool)

  • approximation (Approximation | None)

Return type:

SimulationResult

simulate_batch(*axes, tlist=None, solver=None, options=None, e_ops=None, initial_state=None, backend=None, progress=True, check_truncation=True, truncation_threshold=0.001, approximation=None)[source]

Build and solve a batched sweep. Equivalent to chip.solve_many(seq.build_batch(...)).

backend scopes this one call exactly as in simulate().

The batched solve does not pass through solve_problem(), so the Hilbert-truncation safety net is applied per batch element here (default on); pass check_truncation=False to opt out or retune truncation_threshold.

Parameters:
Return type:

SimulationBatchResult

active_patch(*, hops=1, method='sw')[source]

Reduce the chip to this schedule’s active patch (spectators eliminated).

Convenience for quchip.chip.transformations.active_patch(); see it for the activity rule, elimination order, and validity reporting.

Parameters:
Return type:

ActivePatchResult

clone()[source]

Return a deep copy.

_entries is the single source of truth, so only it is deep-copied; the clone and source sequence share the chip.

Return type:

QuantumSequence

property parameters: Mapping[str, Any]

Chip and scheduled-pulse values available to with_params().

property settings: Mapping[str, Any]

Read-only sequence structure, separate from numerical parameters.

with_params(bindings)[source]

Return a cloned sequence with Chip or pulse.<index> values rebound.

Parameters:

bindings (Mapping[str, Any])

Return type:

QuantumSequence

describe()[source]

Plain-text timeline of the scheduled pulses.

One row per pulse — resolved time window, drive → device, envelope with its declared parameters, carrier frequency — plus a trailing count of delays, barriers, and virtual-Z entries. Returns a string; print(seq.describe()).

Return type:

str

quchip.get_default_backend()[source]

Return the active default backend.

Returns (in order) the nearest _backend_context() override, the user-set default from set_default_backend(), or a freshly instantiated QuTiPBackend (cached for subsequent calls).

Return type:

Backend

quchip.set_default_backend(backend)[source]

Set the active default backend by name ("qutip"/"dynamiqs") or instance.

Parameters:

backend (str | Backend)

Return type:

None

quchip.enable_compilation_cache(path=None, *, min_compile_time_secs=0.01)[source]

Enable JAX’s on-disk persistent compilation cache (opt-in).

JAX compiles each traced kernel (the label_eigensystem scan, eigh / diag / broadcast kernels, dynamiqs solver steps, …) to XLA when it is called for the first time in a process. Those compilations are memoized in memory within a process, but every new process re-pays them. This helper points JAX at a per-user on-disk cache so a later process whose (shape, policy, jaxlib) fingerprint matches loads the compiled executable instead of recompiling.

Fully transparent to jit / grad / vmap — it only changes where compiled artifacts are stored, never traced values or physics. It is opt-in: the package never writes to the user’s disk unless this is called explicitly. JAX keys cache entries on the jaxlib version, so a toolchain upgrade produces fresh entries rather than loading a stale executable.

Parameters:
  • path (str | None) – Cache directory. Defaults to $XDG_CACHE_HOME/quchip/jax (or ~/.cache/quchip/jax) — a per-user path, never a shared/system one, so the cache stays inside the user’s trust boundary.

  • min_compile_time_secs (float) – Skip caching kernels that compile faster than this. The default 0.01 captures quchip’s sub-second kernels that JAX’s ~1s default would otherwise never persist.

Returns:

The absolute cache directory now in use.

Return type:

str

class quchip.CouplingDrive(target=None, *, label=None)[source]

Bases: BaseDrive

Drive authoring base for a two-endpoint coupling Hamiltonian.

Subclasses implement hamiltonian() for the coupling physics they accept. The base class imposes no parametric-interaction requirement.

Parameters:
property device_label: None

Return None because a coupling drive has no device target.

property target_label: str | None

Label of the connected coupling, if any.

connect(target)[source]

Attach this line to a coupling without a device-side handshake.

Parameters:

target (Any)

Return type:

None

class quchip.DeviceDrive(target=None, *, label=None)[source]

Bases: BaseDrive

Drive authoring base for a device-local Hamiltonian.

Parameters:
class quchip.Delay(line, delta_t)[source]

Bases: SignalTransform

Shift every signal on line in time by delta_t ns.

Parameters:
line: str
delta_t: float
apply(signals)[source]

Time-shift every signal on line by delta_t ns.

Parameters:

signals (dict[tuple[str, int], AnalyticSignal])

Return type:

dict[tuple[str, int], AnalyticSignal]

referenced_lines()[source]

Return control-line labels referenced by this transform.

Return type:

tuple[str, …]

to_dict()[source]

Serialize into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct from to_dict() output.

On the registry root, dispatch to the concrete subclass named by data["type"] (forwarding *args / **kwargs). On a concrete subclass, defer to _from_dict_payload(). Concrete subclasses that carry payload override this method directly.

Parameters:

d (dict[str, Any])

Return type:

Delay

class quchip.Gain(line, factor)[source]

Bases: SignalTransform

Scale every signal on line by a complex factor.

Parameters:
line: str
factor: complex
apply(signals)[source]

Scale every signal on line by the complex factor.

Parameters:

signals (dict[tuple[str, int], AnalyticSignal])

Return type:

dict[tuple[str, int], AnalyticSignal]

referenced_lines()[source]

Return control-line labels referenced by this transform.

Return type:

tuple[str, …]

to_dict()[source]

Serialize into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct from to_dict() output.

On the registry root, dispatch to the concrete subclass named by data["type"] (forwarding *args / **kwargs). On a concrete subclass, defer to _from_dict_payload(). Concrete subclasses that carry payload override this method directly.

Parameters:

d (dict[str, Any])

Return type:

Gain

class quchip.ChargeDrive(target=None, *, label=None)[source]

Bases: DeviceDrive

Microwave charge drive on a transmon-like device.

Contributes the standard charge-coupling Hamiltonian

\[H_d(t) = \epsilon(t)\, i(\hat a - \hat a^\dagger)\]

with \(\epsilon(t)\) the in-phase quadrature of the complete delivered classical signal. This is the canonical transmon microwave drive (Koch et al., PRA 76, 042319 (2007); Krantz et al., APR 6, 021318 (2019), Eq. 90).

Examples

>>> from quchip import DuffingTransmon, ChargeDrive
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3)
>>> drive = ChargeDrive(target=q)
>>> drive.target_label == q.label
True
Parameters:
hamiltonian(device, signal)[source]

Map a delivered classical signal to target-local quantum physics.

Parameters:
Return type:

Any

physics_notes()[source]

Return human-readable declarations of this drive’s approximations.

Subclasses append their physical coupling details to the shared target line. Aggregated by Chip.physics_notes().

Return type:

list[str]

class quchip.FluxDrive(target=None, *, label=None)[source]

Bases: DeviceDrive

Real-valued flux drive coupling to \(\hat n\).

The delivered signal’s in-phase quadrature modulates the device frequency through its flux-coupling operator (Koch et al. 2007, Sec. II; Krantz et al. 2019, Sec. V.A on flux tunability).

Examples

>>> from quchip import DuffingTransmon, FluxDrive
>>> q = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3)
>>> flux = FluxDrive(target=q)
>>> flux.target_label == q.label
True
Parameters:
hamiltonian(device, signal)[source]

Map a delivered classical signal to target-local quantum physics.

Parameters:
Return type:

Any

physics_notes()[source]

Return human-readable declarations of this drive’s approximations.

Subclasses append their physical coupling details to the shared target line. Aggregated by Chip.physics_notes().

Return type:

list[str]

class quchip.ParametricDrive(target=None, *, label=None)[source]

Bases: CouplingDrive

Control line pumping a modulable coupling’s strength δ(t) in GHz.

Targets a coupling (object or label string; labels late-bind via Chip.connect()). The scheduled envelope is the real amplitude A(t): with an explicit carrier the pump is δ(t) = A(t)·cos(2π·freq·t - phase); with freq omitted the pump is carrier-free, δ(t) = A(t) directly. Approximation belongs to the chip’s selected engine strategy, not to the drive.

Accepted couplings implement parametric_interaction(); a static coupling raises TypeError naming the hook.

Parameters:
  • coupling (BaseCoupling | str) – Modulable coupling to pump, given as the coupling object or its label. A string label late-binds to the coupling instance via Chip.connect().

  • label (str | None) – Optional explicit label; otherwise auto-generated from "parametric".

  • target (Any)

Raises:

TypeErrorcoupling does not implement parametric_interaction() (a static coupling), or an unexpected keyword argument is passed.

connect(coupling)[source]

Attach this line after confirming that the coupling is modulable.

Parameters:

coupling (Any)

Return type:

None

hamiltonian(coupling, signal)[source]

Map a delivered classical signal to target-local quantum physics.

Parameters:
Return type:

Any

physics_notes()[source]

Return human-readable declarations of this drive’s approximations.

Subclasses append their physical coupling details to the shared target line. Aggregated by Chip.physics_notes().

Return type:

list[str]

class quchip.PhaseDrive(target=None, *, label=None)[source]

Bases: DeviceDrive

Microwave phase drive coupling to \(\hat a + \hat a^\dagger\).

Same carrier machinery as ChargeDrive but with an in-phase (rather than quadrature) coupling. Useful when modelling phase-noise channels or drives whose physical coupling is already referenced to the field quadrature. See Krantz et al. 2019, Sec. IV.A for the two conventions.

Parameters:
hamiltonian(device, signal)[source]

Map a delivered classical signal to target-local quantum physics.

Parameters:
Return type:

Any

physics_notes()[source]

Return human-readable declarations of this drive’s approximations.

Subclasses append their physical coupling details to the shared target line. Aggregated by Chip.physics_notes().

Return type:

list[str]

class quchip.TwoPhotonDrive(target=None, *, label=None)[source]

Bases: DeviceDrive

Parametric two-photon drive for Kerr-cat qubit stabilisation.

Coupling operator: a^2 + a_dag^2

The drive should be scheduled at twice the cavity frequency (freq = 2 * cavity.freq) so that in the rotating frame the interaction is static: eps2(t) * (a_dag^2 + a^2). This combination of Kerr nonlinearity and two-photon drive creates and stabilises cat states.

The engine band-decomposes a^2 + a_dag^2 into excitation weights Delta_n = +2 and Delta_n = -2 and combines them with the delivered signal’s carrier.

The real-field projection contributes only half the scheduled envelope amplitude to each band: the coefficient landing on a_dag^2 + a^2 in the rotating frame is A(t)/2, where A(t) is the amplitude scheduled on this drive’s envelope. Schedule amplitude=2*eps2(t) to realize the target two-photon drive strength eps2(t) used above and in alpha^2 = eps2/K.

Parameters:
  • target (BaseDevice | None) – Device to connect this drive to. None means unconnected.

  • label (str | None) – Optional explicit label; otherwise auto-generated.

References

Examples

>>> from quchip.devices.kerr_cavity import KerrCavity
>>> from quchip.control.drives_two_photon import TwoPhotonDrive
>>> cav = KerrCavity(freq=5.0, kerr=1.0, levels=10, label="cav")
>>> d2 = TwoPhotonDrive(target=cav)
>>> d2.target_label == cav.label
True
hamiltonian(device, signal)[source]

Return the two-photon coupling channel a^2 + a_dag^2.

Parameters:
physics_notes()[source]

Return the base drive notes plus the two-photon coupling declaration.

Return type:

list[str]

class quchip.ControlEquipment(lines, *, signal_chain=None)[source]

Bases: object

Ordered drive lines plus a sequence of signal-chain transforms.

The equipment pipes complete analytic signals through signal_chain before destination drives author Hamiltonian terms.

Parameters:
property lines: list[BaseDrive]

Ordered drive lines (defensive copy).

property signal_chain: list[SignalTransform]

Signal-chain transforms (defensive copy).

apply_signal_chain(signals)[source]

Apply every signal-chain transform to signals, in order.

Each transform receives the previous transform’s output, so transforms compose sequentially: reordering signal_chain changes the result (e.g. a Delay applied before a Gain sees the undelayed signal).

Parameters:

signals (SignalMap) – {(line_label, source_index): AnalyticSignal} map. source_index distinguishes scheduled pulses through mixing.

Returns:

Transformed signal map. May contain keys absent from signals: a Crosstalk transform, for example, adds an entry under the victim drive’s label for every source entry it leaks from.

Return type:

SignalMap

property crosstalks: list[Crosstalk]

Directed crosstalk edges represented by the signal chain.

crosstalk_matrix()[source]

Return a dense matrix view of the Crosstalk transforms.

The matrix uses wiring order (self.lines) as the stable axis ordering. Column index = source drive, row index = victim drive. Diagonal entries are beta=1, theta=0, delay=0 by convention (self-coupling). Off-diagonal entries aggregate every Crosstalk transform present in the signal chain; lines with no corresponding transform contribute zeros.

Non-Crosstalk transforms (Gain, Delay) are ignored here; this is strictly a view of the crosstalk edges.

Returns:

labels (wiring order), beta, theta, delay as [n, n] arrays. Arrays use jax.numpy when any stored entry is a JAX tracer or array, otherwise numpy.

Return type:

CrosstalkMatrix

set_crosstalk_matrix(beta, theta=None, delay=None, *, labels=None)[source]

Rehydrate the crosstalk edges from dense matrices.

Removes every crosstalk transform currently in the signal chain and replaces them with one CrosstalkMatrix. Other signal-chain transforms (Gain, Delay, and user-defined subclasses) are preserved in order.

Parameters:
  • beta (Any) – [n, n] amplitude matrix. beta[i, j] is emitted as the leakage amplitude from source labels[j] onto victim labels[i]. Diagonal entries are ignored (self-coupling belongs to the drive itself, not to a crosstalk edge).

  • theta (Any, optional) – [n, n] phase matrix (radians). Defaults to zeros.

  • delay (Any, optional) – [n, n] delay matrix (ns). Defaults to zeros.

  • labels (tuple[str, ...] | list[str] | None, optional) – Axis ordering. Defaults to wiring order (self.lines). Must match beta.shape[0].

Return type:

None

Notes

Traced JAX entries flow unchanged into CrosstalkMatrix and therefore into the signal-program IR. No concretization occurs.

copy(device_map, coupling_map=None)[source]

Return a structural copy with drive lines rebound to device_map / coupling_map.

Coupling-target lines rebind via coupling_map, keyed by coupling label; device-target lines rebind via device_map.

Parameters:
Return type:

ControlEquipment

to_dict()[source]

Serialize into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d, dev_map, coupling_map=None)[source]

Reconstruct from to_dict() output, rebinding drives via dev_map / coupling_map.

Each line’s target_label is resolved against dev_map first, then coupling_map — device and coupling labels are disjoint by Chip construction, so at most one map holds the label.

Parameters:
Return type:

ControlEquipment

class quchip.Crosstalk(source, victim, beta, theta=0.0, delay=0.0)[source]

Bases: SignalTransform

Linear crosstalk from a source drive line onto a victim line.

For each scheduled operation on the source line, adds

\[\beta\, e^{i\theta}\, s_\mathrm{src}(t - \Delta t)\]

onto the victim line. \(s_\mathrm{src}\) is the complete source signal, including its carrier, phase, and both quadratures. Delaying it therefore includes the carrier phase \(2\pi f\Delta t\) without a separate correction (Balewski et al., arXiv:2502.05362; Sheldon et al., PRA 93, 060302 (2016); Sarovar et al., Quantum 4, 321 (2020)).

Parameters:
  • source (str | BaseDrive) – Source drive or its label.

  • victim (str | BaseDrive) – Victim drive or its label.

  • beta (float) – Leakage amplitude (dimensionless).

  • theta (float) – Phase shift applied to the leaked signal, radians.

  • delay (float) – Time shift of the leaked signal relative to the source, ns.

source: str
victim: str
beta: float
theta: float = 0.0
delay: float = 0.0
apply(signals)[source]

Add the phase-rotated, delayed source signal onto the victim line.

Parameters:

signals (dict[tuple[str, int], AnalyticSignal])

Return type:

dict[tuple[str, int], AnalyticSignal]

referenced_lines()[source]

Return control-line labels referenced by this transform.

Return type:

tuple[str, …]

to_dict()[source]

Serialize into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct from to_dict() output.

On the registry root, dispatch to the concrete subclass named by data["type"] (forwarding *args / **kwargs). On a concrete subclass, defer to _from_dict_payload(). Concrete subclasses that carry payload override this method directly.

Parameters:

d (dict[str, Any])

Return type:

Crosstalk

class quchip.CrosstalkMatrix(labels, beta, theta, delay)[source]

Bases: SignalTransform

Dense crosstalk transform and matrix view in control-line order.

Parameters:
labels

Drive labels in wiring order (the order drives appear in ControlEquipment.lines). Row / column i corresponds to labels[i].

Type:

tuple[str, …]

beta

[n, n] amplitude matrix. beta[i, j] is the leakage amplitude from source labels[j] onto victim labels[i] (column = source, row = victim). Diagonals represent self-coupling and are conventionally 1.0.

Type:

Any

theta

[n, n] phase matrix (radians), same indexing as beta.

Type:

Any

delay

[n, n] delay matrix (ns), same indexing as beta.

Type:

Any

Notes

Every off-diagonal edge reads the same input signal map, so reciprocal entries form one linear mixing stage without recursively leaking one another’s output. Matrix entries flow directly into the signal-program IR (PolarScale/Shift), preserving end-to-end JAX traceability.

labels: tuple[str, ...]
beta: Any
theta: Any
delay: Any
apply(signals)[source]

Apply all directed leakage edges to one shared input snapshot.

Parameters:

signals (dict[tuple[str, int], AnalyticSignal])

Return type:

dict[tuple[str, int], AnalyticSignal]

referenced_lines()[source]

Return control-line labels referenced by this transform.

Return type:

tuple[str, …]

without_line(line)[source]

Return this transform without line, or None when it must be dropped.

Parameters:

line (str)

Return type:

CrosstalkMatrix | None

edges()[source]

Return the directed-edge view used by topology and visualization.

Return type:

list[Crosstalk]

to_dict()[source]

Serialize into a JSON-safe dictionary.

Return type:

dict[str, Any]

classmethod from_dict(d)[source]

Reconstruct from to_dict() output.

On the registry root, dispatch to the concrete subclass named by data["type"] (forwarding *args / **kwargs). On a concrete subclass, defer to _from_dict_payload(). Concrete subclasses that carry payload override this method directly.

Parameters:

d (dict[str, Any])

Return type:

CrosstalkMatrix

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

Bases: object

Sequential dressed-spectrum sweep driver.

Each sweep name is a path from Chip.parameters. At every grid point Chip.with_params() creates an isolated chip and Chip.dress() computes its 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="q.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

class quchip.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.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, ...])

quchip.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.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.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.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.

quchip.analyze_cross_resonance(durations, ctrl0, ctrl1, sigma_ctrl0=None, sigma_ctrl1=None, t_offset=0.0)[source]

Extract CR effective Hamiltonian coefficients from Bloch tomography data.

Fits the target-qubit Bloch trajectory under a CR pulse to the analytic model in bloch_model() — once with the control qubit in |0⟩ and once with it in |1⟩ — and combines the two fits to isolate the six coefficients {IX, IY, IZ, ZX, ZY, ZZ}.

The decomposition is:

p_ctrl0 = (I/2) part → ω_IX, ω_IY, ω_IZ
p_ctrl1 = (Z/2) part → ω_ZX, ω_ZY, ω_ZZ

Specifically:

ω_ZQ = (p_ctrl0_Q - p_ctrl1_Q) / 2 ω_IQ = (p_ctrl0_Q + p_ctrl1_Q) / 2

for Q ∈ {X, Y, Z}.

Parameters:
  • durations ((N,) ndarray) – CR pulse durations in ns (the package convention). Must be monotone; need not be equally spaced. Converted once to seconds at the function boundary for the fit.

  • ctrl0 (dict or 2D ndarray) – Target-qubit Bloch trajectory with control in |0⟩, as a dict with keys "x", "y", "z" or a 2D array shaped (3, N) or (N, 3) (see _as_xyz()).

  • ctrl1 (dict or 2D ndarray) – Same with control in |1⟩.

  • sigma_ctrl0 (dict with keys "x", "y", "z" (optional)) – Per-point standard deviations for the control-|0⟩ data (used as inverse weights in the least-squares fit).

  • sigma_ctrl1 (dict with keys "x", "y", "z" (optional)) – Same for the control-|1⟩ data.

  • t_offset (float) – Subtract this offset (in ns) from durations before fitting (useful to exclude a pulse-ramp transient).

Returns:

Six coefficients in GHz (package units contract; durations are taken in ns and the Hz-valued fit internals are converted exactly once at this boundary), each with a one-sigma uncertainty.

Return type:

CRHamiltonianResult

Raises:

RuntimeError – If all initial guesses fail to converge for either control state.

Examples

>>> import numpy as np
>>> from quchip import analyze_cross_resonance
>>> durations = np.linspace(0, 400, 20)  # ns
>>> ctrl0 = {"x": np.sin(0.02 * durations), "y": np.zeros_like(durations),
...          "z": np.cos(0.02 * durations)}
>>> ctrl1 = {"x": np.sin(0.03 * durations), "y": np.zeros_like(durations),
...          "z": np.cos(0.03 * durations)}
>>> result = analyze_cross_resonance(durations, ctrl0, ctrl1)
>>> zx, zx_err = result.coeffs()["ZX"]  # GHz
quchip.analyze_cr_susceptibility(chip, control, target, *, drive=None)[source]

Return the dressed weak-drive CR response of one directed edge.

The analysis projects the physical control-line operator onto the target’s dressed 0 -> 1 transition twice: once with the control in |0> and once in |1>. No pulse, rotating-frame solve, or time evolution is performed.

Parameters:
  • chip (Chip) – Coupled chip with attached control equipment.

  • control (str | 'BaseDevice') – Directed CR control and target, supplied as device objects or labels.

  • target (str | 'BaseDevice') – Directed CR control and target, supplied as device objects or labels.

  • drive (str | 'BaseDrive' | None) – Control line to project. When omitted, the unique wired device-target line attached to control is selected.

Returns:

Conditional matrix elements and the corresponding IX and ZX coefficients per unit programmed amplitude.

Return type:

CRSusceptibilityResult

Raises:

ValueError – If the edge is a self-edge, control equipment is absent, implicit drive resolution is ambiguous, or the selected line does not target the control.

References

Magesan and Gambetta, Phys. Rev. A 101, 052308 (2020). Malekakhlagh, Magesan, and McKay, Phys. Rev. A 102, 042605 (2020).

quchip.analyze_dispersive_readout(chi, kappa, tau, *, n_photons=None, eps=None, delta_r=0.0, n_crit=None, levels=2)[source]

Compute closed-form steady-state readout figures of merit from (chi, kappa).

Parameters:
  • chi (Any) – Dispersive pull χ_pull = f_r(qubit |1⟩) f_r(qubit |0⟩) in GHz — 2× the σ_z-convention χ. Take it from eliminate(...).effective_params[qubit]["chi"].

  • kappa (Any) – Resonator linewidth (rate) in 1/ns, e.g. effective_params[...]["kappa"].

  • tau (Any) – Integration time in ns.

  • n_photons (Any) – Target steady-state photon number with the qubit in |0⟩. Exactly one of n_photons and eps must be given; the drive rate is then ε = √(n̄₀·((κ/2)² + δ₀²)).

  • eps (Any) – Readout drive rate in rad/ns (power-user path; exactly one of n_photons and eps).

  • delta_r (Any) – Detuning of the qubit-in-ground resonator from the drive, delta_r = f_r|0 f_drive in GHz — the literature’s Δ_r = ω_r ω_d. 0.0 drives on the qubit-in-ground resonance; positive values place the drive below f_r|0.

  • n_crit (Any) – Critical photon number Δ²/(4g²). When given, the strong-drive collapse chi_eff = chi/(1 + n̄₀/n_crit) is applied and validity reports n_over_ncrit/below_ncrit.

  • levels (int) – Number of qubit levels to compute pointer states for (static Python int — it fixes array shapes; 3 includes |f⟩). The pull of level j is the linear-dispersive chi·j.

Return type:

DispersiveReadoutResult

Raises:

ValueError – If both or neither of n_photons and eps are given (a static argument-presence check — never a traced-value comparison).

Examples

>>> from quchip import analyze_dispersive_readout
>>> ro = analyze_dispersive_readout(chi=0.002, kappa=0.005, tau=500.0, n_photons=2.0)
>>> snr, p_err = ro.snr, ro.assignment_error

chi and kappa are typically taken from eliminate(chip, "readout_res").effective_params[qubit].

quchip.analyze_static_zz(chip, device_a, device_b)[source]

Compute exact static ZZ between two devices, plus its 2nd-order SW pathway attribution.

zz is dispersive_shift(), unchanged — the exact, all-orders residual coupling. pathways decomposes the 2nd-order SW correction to the (1_a, 1_b) diagonal energy into its virtual-intermediate-state contributions (pathway_attribution()), read off a partition that keeps only the four computational states of (a, b) (every other device grounded) — the natural loss primitive for a calibration sweep that holds zz near zero while some other exchange (e.g. a eliminate()-mediated J) stays on target.

Parameters:
  • chip (Chip)

  • device_a (str or BaseDevice) – The two devices whose static ZZ is analyzed.

  • device_b (str or BaseDevice) – The two devices whose static ZZ is analyzed.

Return type:

StaticZZResult

Examples

>>> from quchip import Capacitive, Chip, DuffingTransmon
>>> from quchip.analysis import analyze_static_zz
>>> q0 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q0")
>>> q1 = DuffingTransmon(freq=5.2, anharmonicity=-0.24, levels=3, label="q1")
>>> chip = Chip([q0, q1], couplings=[Capacitive(q0, q1, g=0.01)])
>>> result = analyze_static_zz(chip, "q0", "q1")
>>> zz = result.zz  # exact residual ZZ, GHz
quchip.effective_hamiltonian(chip, subspace)[source]

Compute the des-Cloizeaux effective Hamiltonian on a user-chosen computational subspace.

Reuses the chip’s dressed spectrum (_compute_array_labeled()) rather than re-diagonalizing: one full-chip diagonalization drives both this and dispersive_shift(). See EffectiveHamiltonianResult for the construction and its exactness guarantee.

Parameters:
  • chip (Chip)

  • subspace (mapping or sequence) – {device: levels} keeps range(levels) of each named device (spectators grounded); a bare sequence of devices keeps each one’s full qubit (Fock 0/1) subspace (spectators grounded). Both accept a device label string or the object itself.

Return type:

EffectiveHamiltonianResult

Examples

>>> from quchip import Capacitive, Chip, DuffingTransmon
>>> from quchip.analysis import effective_hamiltonian
>>> q0 = DuffingTransmon(freq=5.0, anharmonicity=-0.25, levels=3, label="q0")
>>> q1 = DuffingTransmon(freq=5.2, anharmonicity=-0.24, levels=3, label="q1")
>>> chip = Chip([q0, q1], couplings=[Capacitive(q0, q1, g=0.01)])
>>> result = effective_hamiltonian(chip, ["q0", "q1"])
>>> result.h_eff.shape
(4, 4)
class quchip.CRHamiltonianResult(IX, IY, IZ, ZX, ZY, ZZ, IX_err, IY_err, IZ_err, ZX_err, ZY_err, ZZ_err, params_ctrl0=None, params_ctrl1=None, cov_ctrl0=None, cov_ctrl1=None, cost_ctrl0=0.0, cost_ctrl1=0.0)[source]

Bases: object

Store the six CR effective Hamiltonian coefficients in GHz, with uncertainties.

All six quantities are ordinary frequency (not angular); multiply by 2π to convert to rad/ns. The convention matches Sheldon et al. (PRA 93, 060302(R), 2016):

H_eff = (I ⊗ A + Z ⊗ B) / 2

where A = ω_IX X + ω_IY Y + ω_IZ Z and B = ω_ZX X + ω_ZY Y + ω_ZZ Z.

params_ctrl0 / params_ctrl1 hold the raw per-control-state fit output [px, py, pz, td, bx, by, bz] and are diagnostics only: they are in the fit’s internal units (px/py/pz in Hz, td in seconds), not converted.

Parameters:
IX: float
IY: float
IZ: float
ZX: float
ZY: float
ZZ: float
IX_err: float
IY_err: float
IZ_err: float
ZX_err: float
ZY_err: float
ZZ_err: float
params_ctrl0: ndarray | None = None
params_ctrl1: ndarray | None = None
cov_ctrl0: ndarray | None = None
cov_ctrl1: ndarray | None = None
cost_ctrl0: float = 0.0
cost_ctrl1: float = 0.0
coeffs()[source]

Return {name: (value_ghz, err_ghz)} for all six coefficients.

Return type:

dict[str, tuple[float, float]]

summary()[source]

Print and return a formatted summary of all six coefficients.

Return type:

str

class quchip.CRSusceptibilityResult(m_control_0, m_control_1, IX_per_amplitude, ZX_per_amplitude, control, target, drive)[source]

Bases: object

Store weak-drive CR coefficients per unit programmed drive amplitude.

For control-state-conditioned target-transition matrix elements

\[m_z = \langle\widetilde{z_c,1_t}|D_c|\widetilde{z_c,0_t}\rangle,\]

the effective-Hamiltonian convention

\[H_\mathrm{eff} = \tfrac{1}{2}(IX\,I\!X + ZX\,Z\!X)\]

gives IX_per_amplitude = m_0 + m_1 and ZX_per_amplitude = m_0 - m_1. Values are backend-native complex scalars and remain JAX-traceable.

Parameters:
  • m_control_0 (Any)

  • m_control_1 (Any)

  • IX_per_amplitude (Any)

  • ZX_per_amplitude (Any)

  • control (str)

  • target (str)

  • drive (str)

m_control_0, m_control_1

Dressed target-transition matrix elements conditioned on the control occupying |0> and |1>.

IX_per_amplitude, ZX_per_amplitude

Weak-drive Pauli coefficients per unit signal amplitude.

control, target, drive

Resolved labels.

m_control_0: Any
m_control_1: Any
IX_per_amplitude: Any
ZX_per_amplitude: Any
control: str
target: str
drive: str
class quchip.DispersiveReadoutResult(pointer_states, photon_numbers, sigma, snr, assignment_error, dephasing_rate, chi_eff, validity, notes)[source]

Bases: object

Store steady-state dispersive-readout figures of merit.

All numeric fields stay in the array namespace of the inputs (JAX in, JAX out), so the whole result traces under jax.jit/grad. validity holds {"n_over_ncrit", "below_ncrit"} when n_crit was given — below_ncrit is then a traced boolean under jit/grad; read it outside the traced region or branch with jnp.where — and is empty otherwise. chi here is χ_pull (f_r|1 f_r|0, 2× the σ_z-convention χ).

Parameters:
pointer_states

Complex α_j in the IQ plane, shape (levels,).

Type:

Any

photon_numbers

Steady-state photons |α_j|², shape (levels,).

Type:

Any

sigma

Integrated vacuum-noise blob width 1/√(2κτ) (dimensionless, α-plane units).

Type:

Any

snr

|α₁ α₀|·√(2κτ).

Type:

Any

assignment_error

½·erfc(SNR/(2√2)) — optimal linear discriminant between two equal Gaussians.

Type:

Any

dephasing_rate

Measurement-induced dephasing Γ_m = κ·|α₁ α₀|²/2 in 1/ns.

Type:

Any

chi_eff

chi, or chi/(1 + n̄₀/n_crit) when n_crit was given (GHz).

Type:

Any

validity

See above.

Type:

dict[str, Any]

notes

Explicitly dropped physics.

Type:

list[str]

pointer_states: Any
photon_numbers: Any
sigma: Any
snr: Any
assignment_error: Any
dephasing_rate: Any
chi_eff: Any
validity: dict[str, Any]
notes: list[str]
summary()[source]

Print and return a formatted summary.

Concrete values only — call it outside jax.jit/grad regions.

Return type:

str

class quchip.EffectiveHamiltonianResult(h_eff, basis, device_labels)[source]

Bases: object

Store the des-Cloizeaux effective Hamiltonian on a labeled computational subspace.

h_eff is built as S^-1/2 (W E W^dagger) S^-1/2 with W the overlap block between the requested bare states and their assigned dressed states, E the labeled dressed energies, and S = W W^dagger the (generally non-orthonormal) overlap Gram matrix — the symmetric (Löwdin) orthonormalization of exact_reduction()’s pairwise construction, generalized to an arbitrary number of kept states. S^-1/2 W is unitary by construction, so h_eff is unitarily similar to diag(E): its eigenvalues are exactly the labeled dressed energies, to numerical precision, regardless of how strongly the kept states hybridize with the rest of the chip. Off-diagonal entries carry the effective couplings between kept states; the diagonal is not, in general, individually equal to any one dressed energy once couplings mix the kept states.

Parameters:
h_eff

Dense Hermitian matrix, GHz, ordered as basis.

Type:

Any

basis

Full chip-length bare-occupation tuples spanning the subspace, in h_eff row/column order.

Type:

tuple[tuple[int, …], …]

device_labels

Chip device labels in tensor-product order, for reading basis tuples.

Type:

tuple[str, …]

h_eff: Any
basis: tuple[tuple[int, ...], ...]
device_labels: tuple[str, ...]
describe()[source]

Print and return the matrix with labeled row/column kets.

Concrete values only — call outside jax.jit/grad regions; a traced matrix renders as <traced>.

Return type:

str

class quchip.StaticZZResult(zz, pathways, device_a, device_b, device_labels)[source]

Bases: object

Store exact static ZZ between two devices, plus its 2nd-order SW pathway attribution.

Parameters:
zz

E(1,1) - E(1,0) - E(0,1) + E(0,0), identical to dispersive_shift(device_a, device_b)() — exact, not perturbative.

Type:

Any

pathways

(bare_occupation, amount) pairs: the contribution of each virtual intermediate state to the 2nd-order SW correction of the (1_a, 1_b) diagonal matrix element, amount = 1/2 * V_ik*V_ki*(1/(E_i - E_k) + 1/(E_i - E_k)) for i the (1_a, 1_b) bare index — a decomposition of that one energy correction, not of zz itself (which combines four dressed energies exactly). bare_occupation is a full chip-length Fock tuple, in device order.

Type:

list[tuple[tuple[int, …], Any]]

device_a, device_b

Resolved device labels.

device_labels

Chip device labels in tensor-product order, for reading pathways’ occupation tuples.

Type:

tuple[str, …]

Amounts stay in the array namespace of the chip's parameters (JAX in, JAX
out) traceable and differentiable, precision-filtered only on the
concrete path (:func:`~quchip.chip.sw.pathway_attribution`).
zz: Any
pathways: list[tuple[tuple[int, ...], Any]]
device_a: str
device_b: str
device_labels: tuple[str, ...]
describe()[source]

Print and return the exact ZZ plus the leading virtual pathways.

Concrete values only — call outside jax.jit/grad regions; traced amounts render as <traced>.

Return type:

str

class quchip.PortTone(port, freq, amplitude, _index)[source]

Bases: object

A fixed coherent input field entering through one declared port.

Parameters:
port: str
freq: Any
amplitude: Any
class quchip.VNA(chip, *, input, outputs)[source]

Bases: object

Sweep one input port and report complex output-field response.

Parameters:
  • chip (Any)

  • input (Any)

  • outputs (list[Any] | tuple[Any, ...])

pump(port, *, freq, amplitude)[source]

Add a fixed background tone and return its variation handle.

Parameters:
Return type:

PortTone

vary(tone, parameter, values)[source]

Create a sweep axis for one fixed tone’s frequency or amplitude.

Parameters:
Return type:

Sweep

static zip(*variations)[source]

Pair tone variations element by element.

Parameters:

variations (Sweep)

Return type:

ZippedSweep

sweep(frequencies, *variations, amplitude=None, options=None, progress=False)[source]

Compute differential or finite-amplitude scattering response.

Parameters:
Return type:

SParameterResult

output_spectrum(output, *, frequencies, options=None)[source]

Return the normally ordered output fluctuation spectrum.

Frequencies are offsets in GHz from the stationary tone frame. The coherent carrier is reported separately because it is a delta peak, not a finite sampled spectral density.

Parameters:
  • output (Any)

  • frequencies (Any)

  • options (dict | None)

Return type:

OutputSpectrumResult

g1(output, delays, *, input=None, options=None)[source]

Return normalized first-order output coherence.

Parameters:
  • output (Any)

  • delays (Any)

  • input (Any | None)

  • options (dict | None)

Return type:

OutputCorrelationResult

g2(output, delays, *, input=None, options=None)[source]

Return normalized second-order output intensity correlation.

Parameters:
  • output (Any)

  • delays (Any)

  • input (Any | None)

  • options (dict | None)

Return type:

OutputCorrelationResult

class quchip.ModelMapping[source]

Bases: object

Base class for a single third-party <-> quchip device conversion.

source

Registry key (see source_key()) of the third-party type this mapping imports from. None means the mapping supports export only.

Type:

str or None

target

The quchip device type this mapping exports to. Required whenever export_model() is overridden; None means import-only.

Type:

type or None

library

Name of the third-party library this mapping exports for. Defaults to source.split(".")[0] when source is set; export-only mappings must set it explicitly.

Type:

str or None

Subclassing registers the mapping automatically
\* setting ``source`` registers it for :func:`import_object` under that

key; a second subclass reusing the same source raises TypeError at class-definition time.

\* overriding :meth:`export_model` registers it for :func:`export_object`

under (library, target); overriding without setting target raises TypeError, overriding without a resolvable library (no source to default it from) raises TypeError, and a second subclass reusing the same (library, target) pair raises TypeError naming both classes.

The abstract base itself (``source is None`` and no ``export_model``
override) registers nothing.
source: ClassVar[str | None] = None
target: ClassVar[type | None] = None
library: ClassVar[str | None] = None
import_model(obj, **opts)[source]

Convert third-party object obj into a quchip device.

Override to support import. The base implementation raises NotImplementedError.

Parameters:
Return type:

Any

export_model(device, **opts)[source]

Convert quchip device into a third-party object.

Override to support export; overriding requires setting target. The base implementation raises NotImplementedError.

Parameters:
Return type:

Any

class quchip.EigenbasisDevice(energies, *, charge_operator=None, phase_operator=None, levels=None, label=None, source_type=None, collapse_model='fermi_golden', coupling_channel=None, collapse_rate_threshold=1e-08, **noise)[source]

Bases: BaseDevice

Device backed by frozen energies and optional energy-basis operators.

This is the narrow import path for a third-party model that quchip cannot reconstruct from symbolic circuit parameters. Its authored local space is already the source model’s energy basis, so normal engine materialization applies without a device-owned diagonalization or projection path.

Parameters:
  • energies (Any)

  • charge_operator (Any | None)

  • phase_operator (Any | None)

  • levels (int | None)

  • label (str | None)

  • source_type (str | None)

  • collapse_model (Literal['fermi_golden', 'ladder'])

  • coupling_channel (Literal['charge', 'flux'] | None)

  • collapse_rate_threshold (float)

  • noise (Any)

tunable_param_names = ()

Bare parameters this device exposes as differentiable / tunable scalars. fit_a_dress walks this tuple to discover what it is allowed to optimize on each device, decoupling the inverse-design surface from any specific device model. Three states, keyed on whether the value is explicitly declared:

  • No explicit declaration anywhere in the DeviceModel lineage — the default is derived: every declared parameter() field, in declaration order (see DeviceModel.__init_subclass__).

  • Explicit tuple on the class or an ancestor — exact curation, validated at class-definition time; authoritative and inherited until a subclass explicitly replaces it.

  • Explicit empty tuple — deliberately freezes the device (and its subclasses, until one replaces it) out of inverse design.

On a plain (non-DeviceModel) BaseDevice subclass there is no derivation; the default stays empty unless the subclass declares its own tuple — e.g. Fluxonium uses ("E_C", "E_J", "E_L", "phi_ext").

dissipation(op, p)[source]

Return the common T1, T2, and thermal device channels.

Parameters:
Return type:

tuple[CollapseChannel, …]

unresolved_hamiltonian()[source]

Return the frozen source spectrum as the authored Hamiltonian.

Return type:

PhysicsExpr

property freq: Any

Return the stored zero-to-one transition in GHz.

eigenenergies()[source]

Return the stored ground-shifted energy table.

Return type:

Any

eigenvectors()[source]

Return the identity map because the authored basis is energy ordered.

Return type:

Any

charge_coupling_operator()[source]

Return the supplied charge-like operator in the authored basis.

Return type:

Any

phase_coupling_operator()[source]

Return the supplied phase-like operator in the authored basis.

Return type:

Any

physics_notes()[source]

Return human-readable declarations of this device’s approximations.

Each entry names a non-obvious assumption, approximation, or truncation that a user of this device should be aware of — e.g. “Hilbert truncation: 3 levels”, a model regime (Duffing), or a noise-channel selection (charge- vs flux-coupled T1).

The baseline entry is _truncation_note(), since every BaseDevice has some form of Hilbert-space truncation. A pure-dephasing note is added when T2 is set, since the number-operator dephasing model carries non-obvious assumptions. Subclasses super().physics_notes() and append their own model-specific notes; no registry / engine-side dispatch is needed.

Return type:

list[str]

to_dict()[source]

JSON-safe serialization; subclasses extend with their own parameters.

Return type:

dict[str, Any]

classmethod from_dict(data)[source]

Reconstruct from to_dict() output.

On the registry root, dispatch to the concrete subclass named by data["type"] (forwarding *args / **kwargs). On a concrete subclass, defer to _from_dict_payload(). Concrete subclasses that carry payload override this method directly.

Parameters:

data (dict[str, Any])

Return type:

EigenbasisDevice

quchip.from_scqubits(obj, **opts)[source]

Convert a scqubits object into the matching quchip device.

Dispatches on type(obj) through the ModelMapping registry. Keyword options (levels, label, noise parameters) are forwarded to the mapping’s import_model.

Raises:
Parameters:
Return type:

Any

quchip.to_scqubits(device_or_chip, **opts)[source]

Convert a quchip device into the matching scqubits object.

Dispatches on type(device_or_chip) through the export registry for the "scqubits" library. Parameters must be concrete: a device carrying JAX tracers (inside jit/grad) raises ValueError.

A Chip exports to an scqubits HilbertSpace (devices as subsystems, couplings as interaction terms); see export_chip().

Raises:
Parameters:
  • device_or_chip (Any)

  • opts (Any)

Return type:

Any

quchip.plot_energy_levels(chip, *, ax=None, max_states=None)[source]

Plot dressed chip eigenenergies relative to the ground state.

Each level is rendered as a horizontal bar annotated with its dressed represented-basis tuple label |n_1 n_2 ...> — the per-device bare-basis assignment selected by the chip’s dressing analysis (see chip.analysis). The y-axis is energy in GHz; the ground state is shifted to zero.

Parameters:
  • chip (Chip) – The chip whose dressed spectrum should be plotted.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • max_states (int, optional) – How many of the lowest-lying levels to show. Defaults to min(12, total_levels).

Returns:

The figure holding the energy-ladder axes (ax.figure when ax was given).

Return type:

Figure

Examples

>>> import quchip as qc
>>> from quchip.viz.chip import plot_energy_levels
>>> qb0 = qc.DuffingTransmon(freq=5.0, anharmonicity=-0.3, levels=3)
>>> qb1 = qc.DuffingTransmon(freq=5.2, anharmonicity=-0.3, levels=3)
>>> chip = qc.Chip([qb0, qb1], [qc.Capacitive(qb0, qb1, g=0.005)])
>>> fig = plot_energy_levels(chip, max_states=6)
quchip.plot_expectation(result, *, keys=None, ax=None, linewidth=2.5, legend=True, real=True)[source]

Plot dict-form expectation values over time.

The x-axis is time (ns); the y-axis is observable_traces [key].values — the post-processed recorded trace for each observable O registered in the solver’s e_ops dict. This is not unconditionally Tr(O rho(t)): depending on how the observable was requested, .values may already include demodulation, phase correction, or band summation (see ObservableTrace; its .raw field holds the pre-processing quantity instead). When real is True (the default) only Re of the trace is drawn; when False both the real part (solid) and imaginary part (dashed, lower alpha) are drawn in the same colour per key.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with e_ops passed as a dict.

  • keys (list, optional) – Each entry is either a bare key ("cav") or a (key, index) tuple selecting one element of a list-valued observable, matched against the registered observable_traces keys first — see _collect_expectation_traces(). String and device/drive keys are resolved with resolve_label so both are accepted interchangeably. Defaults to every registered trace.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • linewidth (float) – Line width for every trace.

  • legend (bool) – Whether to draw a legend of the plotted keys.

  • real (bool) – When True, draw only the real part of each trace; when False, draw both the real (solid) and imaginary (dashed) parts.

Returns:

The figure holding the expectation-trace axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • TypeErrore_ops was not passed as a dict (observable_traces is None).

  • KeyError – An entry in keys does not resolve to a registered trace or a valid (key, index) selector.

quchip.plot_graph(chip, path='chip_topology.html', *, full=True, exclude=None, values='bare', layout='force_atlas', height='800px', width='100%')[source]

Write an interactive HTML chip topology and return its path.

The rendered graph is a standalone, offline-capable HTML file: devices are drawn as dots and drives as diamonds, with couplings, control wiring, and crosstalk distinguished by edge colour and dash pattern. Node colours are auto-assigned per class (DuffingTransmon, Resonator, ChargeDrive, …) so new device/drive kinds are visually distinct without user intervention.

Layouts:

  • "force_atlas" (default) — force_atlas_2based physics with larger/heavier computational devices so the register sits centrally and auxiliary couplers/drives orbit.

  • "hierarchical" — pyvis hrepulsion on explicit levels (computational devices → other devices/couplings → drives), useful for chips with many auxiliary drives or cross-resonance lines.

exclude takes priority over full; passing full=False with no exclude hides drives and crosstalk (chip-only view). Every coupling is rendered as its own junction node splitting the device-device edge (so an edge-pump control has a node to attach to); excluding couplings also removes any edge-pump controls that would otherwise dangle (see _collect_topology()).

values="bare" preserves the declaration view: device frequencies and coupling strengths come directly from component parameters and require no diagonalization. values="dressed" instead labels devices with dressed \(f_{01}\) and coupling junctions with the full-pull cross-Kerr \(K_{ab}\). values="both" shows both annotations. These modes do not change the topology.

Parameters:
  • chip (Chip) – The chip to visualise.

  • path (str) – Destination .html path. Returned for convenience.

  • full (bool) – If False, hide drives and crosstalk unless exclude is given.

  • exclude (set of str, optional) – Any of {"coupling", "drive", "crosstalk"} to hide entirely.

  • values ({"bare", "dressed", "both"}) – Physical values shown on device and coupling nodes. Defaults to the declared bare values.

  • layout (str) – "force_atlas" or "hierarchical".

  • height (str) – CSS dimensions forwarded to pyvis.network.Network.

  • width (str) – CSS dimensions forwarded to pyvis.network.Network.

Returns:

path, unchanged — returned for convenience so the call can be chained into whatever opens/serves the file.

Return type:

str

Raises:
  • ImportErrorpyvis is not installed.

  • ValueErrorlayout or values is not one of its supported choices.

quchip.plot_populations(result, *, trace_out=None, computational=False, ax=None, linewidth=2.5, legend=True, colors=None, threshold=0.01)[source]

Plot basis-state populations over time.

The figure has time (ns) on the x-axis and population p_n(t) = Tr(|n><n| rho(t)) on the y-axis, one line per represented-basis ket |n> = |n_1 n_2 ...> of the retained subsystems. States whose peak population stays below threshold are hidden; set threshold to 0 to show every state.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate().

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before computing populations. Accepts either device objects or their string labels (UX favourability). Requires options={"store_states": True} on the solver call.

  • computational (bool) – When True, restricts computational subsystems to their {|0>, |1>} subspace.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • linewidth (float) – Line width for every population trace.

  • legend (bool) – Whether to draw a legend of the visible states.

  • colors (dict, optional) – Per-state colour overrides, keyed by the same basis-state tuples used internally; unlisted states fall back to a tab20 cycle.

  • threshold (float) – Populations whose time-max falls below this value are omitted.

Returns:

The figure holding the population-trace axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • RuntimeErrortrace_out is given but no states were stored (pass options={"store_states": True} to the solver).

  • ValueErrortrace_out would remove every subsystem.

quchip.plot_sequence(sequence, *, ax=None, lane_order=None, color_by='device')[source]

Plot a QuantumSequence as a lane chart.

Each row (“lane”) is a (target_label, drive_label) channel; each bar is one scheduled envelope, drawn from its start_time to start_time + envelope.duration on the x-axis (ns), annotated with the envelope class name and carrier frequency in GHz. Idle channels that were created (channel_cursors advanced) but never scheduled show up as empty lanes.

Parameters:
  • sequence (QuantumSequence) – The schedule to visualise.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • lane_order (list of (target_label, drive_label), optional) – Explicit ordering (top-to-bottom). Defaults to sorted lane keys.

  • color_by ({"device", "line"}) – Group bars by target device or by drive line for colour mapping.

Returns:

The figure holding the lane-chart axes (ax.figure when ax was given).

Return type:

Figure

Raises:

ValueErrorcolor_by is not "device" or "line".

quchip.plot_state(result, index, *, trace_out=None, computational=False, mode='population', ax=None, cmap='RdBu_r', color=None)[source]

Plot a single stored state at time-index index.

Two modes are supported:

  • "population" — a bar chart of diagonal elements p_n.

  • "dm" — side-by-side heatmaps of Re(rho) and Im(rho); the colormap cmap is divergent, and both heatmaps share one symmetric normalization (vmin=-m, vmax=+m for m = max(|Re(rho)|, |Im(rho)|)) so their colours are directly comparable — an entry that looks equally saturated in both panels really is equal in magnitude.

When both computational and non-computational subsystems are present, pass trace_out to focus on a target register and/or computational = True to restrict to the {|0>, |1>} subspace on computational devices (see Nielsen & Chuang, Ch. 2).

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with options={"store_states": True}.

  • index (int) – Stored-time index to plot. Supports Python-style negative indexing (-1 is the last stored time); must satisfy -N <= index < N for N = len(result.times).

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before plotting.

  • computational (bool) – When True, restricts computational subsystems to their {|0>, |1>} subspace.

  • mode ({"population", "dm"}) – Which representation to draw.

  • ax (matplotlib.axes.Axes, optional) – For mode="population": a single axes (or None for a new figure). For mode="dm": an iterable of exactly two axes (real_ax, imag_ax) (or None for a new 1x2 figure).

  • cmap (str) – Divergent colormap for mode="dm" heatmaps.

  • color (str, optional) – Bar colour override for mode="population". Defaults to a per-state tab10 cycle.

Returns:

The figure holding the plotted axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • IndexErrorindex is outside [-N, N) for N = len(result.times).

  • ValueErrormode is not "population" or "dm", or trace_out would remove every subsystem.

  • RuntimeError – No states were stored (pass options={"store_states": True} to the solver).

quchip.plot_wavefunction(device, n, *, ax=None, color=None)[source]

Plot the represented-basis probability weights of eigenstate n.

Shows |<k|psi_n>|^2 for each bare basis state |k>, k = 0 ... levels - 1, where |k> indexes whatever basis device.hamiltonian() is expressed in. Every stock device model shipped with quchip — including DuffingTransmon, ChargeBasisTransmon, Fluxonium, and Resonator — returns a Hamiltonian that is already diagonal in its own retained eigenbasis, so eigenstate n is exactly |n> and the bar chart is a single delta bar for every one of them: this plot cannot show “mixing” for any built-in model. Mixing becomes visible only for a custom device whose hamiltonian() returns a matrix that is not diagonal in its represented basis.

Parameters:
  • device (BaseDevice) – The device whose eigenstates are diagonalised.

  • n (int) – Eigenstate index (0 <= n < device.levels).

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • color (str, optional) – Bar colour. Defaults to a per-index tab10 cycle.

Returns:

The figure holding the bar-chart axes (ax.figure when ax was given).

Return type:

Figure

Raises:

IndexErrorn is outside [0, device.levels).

Examples

>>> import quchip as qc
>>> transmon = qc.DuffingTransmon(freq=5.0, anharmonicity=-0.3, levels=4)
>>> transmon.plot_wavefunction(n=1)
quchip.plot_wigner(result, index=-1, *, trace_out=None, xvec=None, yvec=None, ax=None, cmap='RdBu_r', colorbar=True)[source]

Plot the Wigner quasi-probability distribution of a stored state.

Axes are the phase-space quadratures x (position-like) and p (momentum-like); the colourmap is divergent and symmetric about zero so negative regions — the hallmark of non-classical states — stand out directly.

When xvec is not supplied the plot window is auto-sized from the mean photon number <n> = Tr(rho n_hat) of the reduced state (computed directly from diag(rho) to avoid an O(d^2) matmul for a diagonal-only observable), extending to at least +/-3.

Exactly one subsystem must remain after trace_out — a Wigner function is a single-mode phase-space picture, and its basis indices are interpreted directly as photon numbers n = 0, 1, 2, .... This is checked; what is not, and cannot be, checked from result metadata alone is the remaining precondition: the retained subsystem’s represented basis must actually be a photon-number ladder (true for a bosonic mode such as Resonator, false for a device whose represented basis is not Fock, e.g. a charge- or flux-basis qubit) — passing such a device silently produces a Wigner-shaped plot with no such physical meaning.

Parameters:
  • result (SimulationResult) – Output of quchip.engine.simulate(), with options={"store_states": True}.

  • index (int) – Stored-time index to plot. Supports Python-style negative indexing (-1, the default, is the last stored time); must satisfy -N <= index < N for N = len(result.times).

  • trace_out (device, label, or list thereof, optional) – Subsystems to partial-trace over before plotting. Required whenever more than one subsystem is stored — see Raises.

  • xvec (ndarray, optional) – Phase-space grids for the x/p quadratures. Defaults to an auto-sized, evenly spaced grid (see above); yvec defaults to xvec when only xvec is given.

  • yvec (ndarray, optional) – Phase-space grids for the x/p quadratures. Defaults to an auto-sized, evenly spaced grid (see above); yvec defaults to xvec when only xvec is given.

  • ax (matplotlib.axes.Axes, optional) – Existing axes to draw onto. When None a new figure is created.

  • cmap (str) – Divergent colormap, symmetric about zero.

  • colorbar (bool) – Whether to attach a colorbar.

Returns:

The figure holding the Wigner-function axes (ax.figure when ax was given).

Return type:

Figure

Raises:
  • IndexErrorindex is outside [-N, N) for N = len(result.times).

  • ValueError – More or fewer than one subsystem remains after trace_out; the message lists the retained device labels and a trace_out value that isolates a single one of them.

  • RuntimeError – No states were stored (pass options={"store_states": True} to the solver).

References

  • Wigner, Phys. Rev. 40, 749 (1932).

  • Cahill & Glauber, Phys. Rev. 177, 1882 (1969).

  • Leonhardt, Essential Quantum Optics (2010), Ch. 3.

Modules

analysis

quchip.analysis — physics analysis tools.

approximations

Explicit strategies for reducing an authored Hamiltonian.

backend

Backend selection and concrete backend implementations.

chip

Chip topology — the composite quantum system and its two-body couplings.

control

Classical control surface: lines, signal chain, and envelopes.

declarative

Declarative physics model API.

devices

Device models — truncated-Hilbert-space quantum systems owned by a chip.

engine

Engine pipeline: Chip ResolvedFrame EngineResult SolveProblem.

extensions

Installed reference implementations for the public extension surfaces.

interop

Third-party model interoperability: ModelMapping authoring surface and registry.

inverse_design

Inverse design from a numerical dressed-chip specification.

results

Backend-agnostic simulation output containers.

sweep

Declarative parameter sweeps for chip studies.

utils

Physical constants and shared utilities.

viz

Lazy-loaded Matplotlib/pyvis visualization helpers.