quchip.declarative¶
Declarative physics model API.
- class quchip.declarative.CouplingModel(device_a, device_b, *, label=None, rwa=None, **params)[source]¶
Bases:
BaseCouplingDeclarative two-body coupling base.
Subclasses declare physics parameters via
parameter()and implementinteraction()(returning aPhysicsExprover the two endpoint operators). The RWA is applied structurally by the chip and engine viarwa_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 aPhysicsExprcarrying a single dynamic source.
coupling_strengthdefaults to the first declared parameter field (suited for the common case of oneg-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.gradarguments. 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
- 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:
- 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
Nonewhen 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:
- parametric_interaction(a, b)[source]¶
Return the parametric interaction structure, or
Nonewhen this coupling is not modulable.The coupling-side mirror of the device drive-dispatch protocols: a
ParametricDriveaccepts any coupling whose hook returns aPhysicsExpr.
- rwa_parametric_interaction(a, b)[source]¶
RWA-retained parametric structure; defaults to the full form.
- 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
Nonewhenparametric_interaction()declines. Valid only once the coupling is chip-resolved (same contract asinteraction_hamiltonian()).
- interaction_hamiltonian()[source]¶
Compile the full interaction expression for the default backend.
- Return type:
- 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.
- 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 whentime_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:
BaseDeviceDeclarative base for physics device models.
Subclasses declare their parameters as annotated class attributes using
parameter()(e.g.freq: Scalar = parameter(positive=True)) and implementlocal_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 bylocal_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
- 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 viaquchip.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,Iand the Pauli handles as composablePhysicsExprnodes.- Returns:
The local Hamiltonian expression, in ordinary-frequency units (GHz).
- Return type:
- hamiltonian()[source]¶
Compile
local_hamiltonian()for the active default backend.- Return type:
- class quchip.declarative.DynamicScalar(source)[source]¶
Bases:
objectA time-dependent scalar payload attached to a
PhysicsExpr.sourceis the time-dependent object (typically an envelope or aScalarModulation) that the engine compiles into aDynamicTermwhen assembling the Hamiltonian. Multiplying aDynamicScalarby aPhysicsExprrecords the source on the resulting expression’sdynamic_sourcestuple — the operator structure itself is unchanged.- Parameters:
source (Any)
- class quchip.declarative.LocalOps(label, levels)[source]¶
Bases:
objectDeclarative operator namespace for one local Hilbert space endpoint.
Passed as
optoDeviceModel.local_hamiltonian()and as the two endpointsa,btoCouplingModel.interaction(). Each property returns aPhysicsExprthat 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'
- 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.
- class quchip.declarative.Parameter(default=<object object>, positive=False, nonnegative=False, serialize=True, unit=None)[source]¶
Bases:
objectMetadata 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.
- class quchip.declarative.PhysicsExpr(kind, args=(), labels=(), scalar=1.0, dynamic_sources=())[source]¶
Bases:
objectBackend-neutral expression tree for local physics operators.
Built by composing
LocalOpsoperator 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:
- dynamic_sources: tuple[DynamicScalar, ...] = ()¶
- 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.
unitis display metadata for human-readable surfaces such asChip.describe()— the package-wide units contract (GHz, ns, mK) still governs the value itself.Nonemeans dimensionless or unknown. Returns aParameterfield descriptor thatparameter_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:
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
Declarative envelope shape base class. |
|
Backend-neutral operator algebra for the declarative physics DSL. |
|
Declarative base classes for device and coupling physics models. |
|
Declarative operator-handle namespaces for the physics DSL. |
|
Declared-parameter metadata, synthesized |
|
Trace-safe numeric namespace for declarative model authors. |