Diffusion¶
Diffusion objects define the stochastic term in a continuous-time state evolution
where:
- \(x_t \in \mathbb{R}^{d_x}\) is the latent state,
- \(u_t\) is an optional control,
- \(W_t \in \mathbb{R}^{d_w}\) is Brownian motion,
bm_dim = d_w,- and \(L(x_t, u_t, t)\) is the diffusion coefficient.
Dynestyx exposes three structured diffusion classes:
- If your diffusion has the form \(L = \sigma I_{d_w}\) with \(d_w = d_x\), use
ScalarDiffusion(sigma, bm_dim=state_dim). - If your diffusion has the form \(L = \sigma \mathbf{1}_{d_x}\), use
ScalarDiffusion(sigma, bm_dim=1). - If your diffusion has the form \(L = \mathrm{diag}(v)\), use
DiagonalDiffusion(v, bm_dim=state_dim). - If your diffusion has the form \(L = v \in \mathbb{R}^{d_x \times 1}\), use
DiagonalDiffusion(v, bm_dim=1). - If your diffusion is a general matrix \(L \in \mathbb{R}^{d_x \times d_w}\), use
FullDiffusion(L).
The same constructors also accept callables (x, u, t) -> value instead of
constants. For example, if \(L(x_t, u_t, t) = \sigma(x_t) I_{d_w}\), use
ScalarDiffusion(lambda x, u, t: sigma(x), bm_dim=state_dim). The same pattern
applies across ScalarDiffusion, DiagonalDiffusion, and FullDiffusion.
In practice, many models use a constant diffusion coefficient. In those cases, pass the matrix/vector/scalar value directly. Reserve callable diffusion coefficients for cases where the coefficient genuinely depends on state, control, or time.
Diffusion¶
Diffusion is the common base class for structured diffusion coefficients.
Most users should instantiate one of its public subclasses instead of using
Diffusion directly:
- Use
ScalarDiffusionwhen one scalar scale is enough. This is the most common choice for isotropic diffusion. - Use
DiagonalDiffusionwhen each state coordinate should have its own scale but the loading remains axis-aligned. - Use
FullDiffusionwhen you need to specify a genuinely matrix-valued loading.
Each class accepts either:
- a constant coefficient, or
- a callable
(x, u, t) -> value.
FullDiffusion¶
Mathematically, FullDiffusion represents
\(L(x_t, u_t, t) \in \mathbb{R}^{d_x \times d_w}\).
Use FullDiffusion(coefficient, bm_dim=None) when you want to specify this
matrix-valued diffusion coefficient directly.
Accepted coefficient forms:
- a constant array with trailing shape
(state_dim, bm_dim), or - a callable
(x, u, t) -> arraywith trailing shape(state_dim, bm_dim).
If coefficient is constant, bm_dim is inferred automatically when omitted.
Example:
import jax.numpy as jnp
from dynestyx import ContinuousTimeStateEvolution, FullDiffusion
state_evolution = ContinuousTimeStateEvolution(
drift=lambda x, u, t: -x,
diffusion=FullDiffusion(jnp.eye(2)),
)
DiagonalDiffusion¶
Mathematically, DiagonalDiffusion represents a vector-valued coefficient
\(v(x_t, u_t, t) \in \mathbb{R}^{d_x}\). If bm_dim = d_x, this means
\(L = \mathrm{diag}(v(x_t, u_t, t))\). If bm_dim = 1, this means
\(L = v(x_t, u_t, t) \in \mathbb{R}^{d_x \times 1}\).
Use DiagonalDiffusion(coefficient, bm_dim) when the diffusion is naturally
parameterized by a vector of state-wise loadings.
Accepted coefficient forms:
- a constant vector with trailing shape
(state_dim,), or - a callable
(x, u, t) -> arraywith trailing shape(state_dim,).
bm_dim is required and must be either 1 or state_dim.
Example:
import jax.numpy as jnp
from dynestyx import ContinuousTimeStateEvolution, DiagonalDiffusion
state_evolution = ContinuousTimeStateEvolution(
drift=lambda x, u, t: -x,
diffusion=DiagonalDiffusion(jnp.array([0.1, 0.2]), bm_dim=2),
)
ScalarDiffusion¶
Mathematically, ScalarDiffusion represents a scalar-valued coefficient
\(\sigma(x_t, u_t, t) \in \mathbb{R}\). If bm_dim = d_x, this means
\(L = \sigma(x_t, u_t, t)\,I_{d_w}\) with \(d_w = d_x\). If bm_dim = 1, this
means \(L = \sigma(x_t, u_t, t)\,\mathbf{1}_{d_x}\), viewed as a column vector
in \(\mathbb{R}^{d_x \times 1}\).
Use ScalarDiffusion(coefficient, bm_dim) when one scalar scale is enough.
This is usually the simplest choice for isotropic diffusion.
Accepted coefficient forms:
- a scalar,
- a constant array with trailing shape
(1,), or - a callable
(x, u, t) -> scalar_or_length_1_array.
bm_dim is required and must be either 1 or state_dim.
Constant example:
from dynestyx import ContinuousTimeStateEvolution, ScalarDiffusion
state_evolution = ContinuousTimeStateEvolution(
drift=lambda x, u, t: -x,
diffusion=ScalarDiffusion(0.1, bm_dim=2),
)
Callable example:
import jax.numpy as jnp
from dynestyx import ContinuousTimeStateEvolution, ScalarDiffusion
state_evolution = ContinuousTimeStateEvolution(
drift=lambda x, u, t: -x,
diffusion=ScalarDiffusion(
lambda x, u, t: 0.1 + 0.05 * jnp.tanh(x[0]),
bm_dim=2,
),
)
API¶
Diffusion¶
Bases: Module
Base class for diffusion coefficients in SDEs.
A diffusion encapsulates both the numeric coefficient L(x, u, t) and the
structural interpretation of that coefficient inside the SDE
dx_t = f(x_t, u_t, t) dt + L(x_t, u_t, t) dW_t.
Most users should instantiate one of the concrete subclasses:
FullDiffusionfor an arbitrary matrix-valued coefficient.DiagonalDiffusionfor axis-aligned loadings.ScalarDiffusionfor isotropic or shared-driver noise.
The coefficient may be either:
- a constant array or scalar, for time-homogeneous diffusion, or
- a callable
(x, u, t) -> valuefor state-, control-, or time-dependent diffusion.
bm_dim is the Brownian dimension d_w. For FullDiffusion it can be
inferred from the matrix shape. For DiagonalDiffusion and
ScalarDiffusion it must be specified explicitly and must be either 1 or
state_dim.
coefficient_event_rank: int | None
property
¶
The per-member event rank of a constant coefficient (None if callable).
The event rank is the number of trailing axes that make up one member's
coefficient, independent of any leading plate batch axes (FullDiffusion
is matrix-valued so rank 2, DiagonalDiffusion vector-valued so rank 1,
ScalarDiffusion scalar so rank 0). It lets the plate machinery tell a
shared coefficient from a genuinely per-member one even when its shape
coincides with the plate sizes. None for callable coefficients, whose
value is not a sliceable pytree leaf.
This is a derived property rather than a stored field beacuse of interactions with effectful and equinox.
FullDiffusion¶
Bases: Diffusion
General matrix-valued diffusion coefficient.
Use FullDiffusion when you want to specify the diffusion matrix
L(x, u, t) directly.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
DiffusionSpec
|
Either a constant array with trailing shape
|
required |
bm_dim
|
int | None
|
Optional Brownian dimension. If omitted for a constant coefficient, it is inferred from the trailing matrix dimension. |
None
|
This is the most general public diffusion class. Prefer it when your model genuinely needs a dense or otherwise unstructured loading matrix.
DiagonalDiffusion¶
Bases: Diffusion
Vector-valued diffusion with diagonal or shared-driver interpretation.
Use DiagonalDiffusion(v, bm_dim=...) when the diffusion is naturally
parameterized by a vector v of length state_dim.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
DiffusionSpec
|
Either a constant vector with trailing shape
|
required |
bm_dim
|
int | None
|
Brownian dimension. Must be either |
None
|
- If
bm_dim = state_dim, the vector is interpreted as the diagonal ofL = diag(v). - If
bm_dim = 1, the vector is interpreted as a column loading vector, so all state coordinates share one Brownian driver.
This is often the right public API choice when each state coordinate has its own scale but you do not want to write out a full matrix.
ScalarDiffusion¶
Bases: Diffusion
Scalar-valued diffusion with isotropic or shared-driver interpretation.
Use ScalarDiffusion(sigma, bm_dim=...) when the diffusion is controlled
by a single scalar scale sigma.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
coefficient
|
DiffusionSpec
|
Either a scalar, a constant array with trailing shape
|
required |
bm_dim
|
int | None
|
Brownian dimension. Must be either |
None
|
- If
bm_dim = state_dim, this represents isotropic diffusionL = sigma I. - If
bm_dim = 1, this represents a shared scalar driver applied equally to every state coordinate.
This is typically the simplest public API choice for constant isotropic
diffusion, and is usually preferable to writing sigma * eye(state_dim)
by hand.