quchip.declarative.models

Declarative base classes for device and coupling physics models.

DeviceModel and CouplingModel let a subclass declare its physics parameters as annotated class attributes via parameter() and implement only the Hamiltonian expression; both synthesize their own __init__, JAX pytree registration (for DeviceModel), and post-construction sign validation from the declared fields.

Classes

CouplingModel(device_a, device_b, *[, ...])

Declarative two-body coupling base.

DeviceModel(*[, levels, label, T1, T2, ...])

Declarative base for physics device models.

class quchip.declarative.models.DeviceModel(*, levels=2, label=None, T1=None, T2=None, thermal_population=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):
...         return self.freq * op.n + 0.5 * self.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 (str | None)

  • T1 (Any)

  • T2 (Any)

  • thermal_population (Any)

  • params (Any)

approximation: 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: bool = False

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

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)[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.

Returns:

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

Return type:

PhysicsExpr

hamiltonian()[source]

Compile local_hamiltonian() for the active default backend.

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]

class quchip.declarative.models.CouplingModel(device_a, device_b, *, label=None, rwa=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 RWA is applied structurally by the chip and engine via rwa_keeps_band(); only the parametric RWA structures remain author-declared, because pump sideband selection depends on frequency intent rather than operator structure: which sideband a pump activates (red: Δa+Δb=0 exchange; blue: |Δa+Δb|=2 two-photon) depends on where the pump frequency sits relative to traced device frequencies, and a structural rule cannot infer it without branching on traced values. Optional overrides:

  • time_dependent() — parametric (time-dependent) modulation, as a PhysicsExpr carrying a single dynamic source.

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):
...         return self.g * (a.a * b.adag + a.adag * b.a)
>>> c = ExchangeCoupling("q0", "q1", g=0.01)
>>> c.coupling_strength
0.01
Parameters:
  • device_a (Any)

  • device_b (Any)

  • label (str | None)

  • rwa (bool | None)

  • params (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)[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).

Returns:

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

Return type:

PhysicsExpr

time_dependent(a, b)[source]

Return an optional time-dependent interaction expression.

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

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

Returns:

An expression carrying exactly one dynamic source (envelope or modulation), or None when the coupling is purely static.

Return type:

PhysicsExpr or None

rwa_time_dependent(a, b)[source]

Return the RWA time-dependent expression, defaulting to full form.

Mirrors the parametric RWA split for the dynamic term: subclasses whose parametric drive keeps only the co-rotating operators under RWA override this.

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

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

Return type:

Any

parametric_interaction(a, b)[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

rwa_parametric_interaction(a, b)[source]

RWA-retained parametric structure; defaults to the full form.

Parameters:
Return type:

Any

parametric_operator(chip)[source]

Compile the parametric structure for chip’s resolved RWA policy.

Returns the backend-native operator on the local two-body space, or None when parametric_interaction() declines. Valid only once the coupling is chip-resolved (same contract as interaction_hamiltonian()).

Parameters:

chip (Any)

Return type:

Any | None

interaction_hamiltonian()[source]

Compile the full interaction expression for the default backend.

Return type:

Any

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]

dynamic_interaction_terms(chip)[source]

Compile the optional single-source dynamic interaction term.

Parameters:

chip (Chip) – Owning chip, consulted via Chip.resolve_rwa() to select the static or RWA operator structure of the dynamic term.

Returns:

One (static_operator, scalar_modulation) pair when time_dependent() returns an expression, else an empty list.

Return type:

list of (operator, modulation)