quchip.declarative

Declarative physics model API.

class quchip.declarative.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:

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)

class quchip.declarative.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.DynamicScalar(source)[source]

Bases: object

A time-dependent scalar payload attached to a PhysicsExpr.

source is the time-dependent object (typically an envelope or a ScalarModulation) that the engine compiles into a DynamicTerm when assembling the Hamiltonian. Multiplying a DynamicScalar by a PhysicsExpr records the source on the resulting expression’s dynamic_sources tuple — the operator structure itself is unchanged.

Parameters:

source (Any)

source: Any
quchip.declarative.EndpointOps

alias of LocalOps

class quchip.declarative.LocalOps(label, levels)[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
>>> op = LocalOps(label="q", levels=3)
>>> H = 5.0 * op.n + 0.5 * (op.adag @ op.adag @ op.a @ op.a)
>>> H.kind
'add'
Parameters:
label: str
levels: int
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 I: PhysicsExpr

Identity operator for this endpoint.

property x: PhysicsExpr

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

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.

quchip.declarative.Modulation

alias of Any

class quchip.declarative.Parameter(default=<object object>, positive=False, nonnegative=False, serialize=True, unit=None)[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 = <object object>
positive: bool = False
nonnegative: bool = False
serialize: bool = True
unit: str | None = None
property has_default: bool

Return whether this field has a declared default value.

class quchip.declarative.PhysicsExpr(kind, args=(), labels=(), scalar=1.0, dynamic_sources=())[source]

Bases: object

Backend-neutral expression tree for local physics operators.

Built by composing LocalOps operator handles, not constructed directly. Operators on the same endpoint compose with @ (matrix product); operators on different endpoints combine with * (tensor product). Scalars scale via *, and an envelope or modulation multiplied in is recorded as a dynamic source rather than altering the operator structure.

Examples

>>> from quchip.declarative import LocalOps
>>> a, b = LocalOps("q0", 3), LocalOps("q1", 3)
>>> coupling = 0.01 * (a.a * b.adag + a.adag * b.a)
>>> coupling.kind
'add'
>>> coupling.labels
('q0', 'q1')
Parameters:
kind: str
args: tuple[Any, ...] = ()
labels: tuple[str, ...] = ()
scalar: Any = 1.0
dynamic_sources: tuple[DynamicScalar, ...] = ()
without_dynamic_sources()[source]

Return the same operator expression with dynamic sources stripped.

Return type:

PhysicsExpr

has_dynamic_source()[source]

Return whether this expression carries any time-dependent source.

Return type:

bool

quchip.declarative.Scalar

alias of Any

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

Declare a traceable scalar or modulation 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 field is required in the synthesized __init__.

  • 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").

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):
...         return self.freq * op.n
>>> Oscillator(freq=5.0, levels=3).freq
5.0

Modules

envelope_shape

Declarative envelope shape base class.

expr

Backend-neutral operator algebra for the declarative physics DSL.

models

Declarative base classes for device and coupling physics models.

ops

Declarative operator-handle namespaces for the physics DSL.

parameters

Declared-parameter metadata, synthesized __init__ signatures, and validation.

qnp

Trace-safe numeric namespace for declarative model authors.