Overview¶
Simulators run pure-JAX forward trajectories for a
DynamicalModel on a chosen time grid, then optionally attach the realized
outputs to a NumPyro trace when used through dsx.sample(...).
For pure-JAX forward generation, user-facing code should prefer
dsx.simulate(...). The simulator classes documented here are the NumPyro-aware
handler implementations used by dsx.sample(...) and by posterior rollout from
Filter/Smoother.
When to use each time argument
predict_times: use this when you want rollout trajectories at specific times for simulation and/or post-filter rollout.- In posterior-rollout mode, predictions are generated at
predict_timesfrom inference-handler posteriors. - Typical use: forward simulation, forecasting, or dense trajectories for visualization.
obs_times/obs_valuesare consumed by observation-aware handlers such asLatentPathBuilder,Filter, andSmoother, not by the public simulator interface itself.- If
predict_timesis omitted: the simulator does not run and adds no deterministic sites.
Context and caveats
- NumPyro context required for
dsx.sample(...): simulator handlers draw randomness from the active NumPyro PRNG key, but the rollout itself is pure JAX and sites are registered only at the end. Usedsx.simulate(...)for the explicit pure-JAX API. - Generation-only public API: raw
Simulator,DiscreteTimeSimulator,ODESimulator, andSDESimulatorcalls expectpredict_times, not direct observation conditioning. - Inference lives elsewhere: use
LatentPathBuilderfor explicit latent paths,Filterfor marginalized inference, andSmootherfor smoothing. Simulators can then wrap those handlers for rollout withpredict_times.
Deterministic sites
When simulator trajectories are produced, sites are recorded as "{name}_{key}"
where name is the first
argument to dsx.sample(name, dynamics, ...) (conventionally "f"):
"f_x_0": realized initial-state draw, shape(n_sim, state_dim),"f_times": trajectory time grid, shape(n_sim, T),"f_states": latent trajectory, shape(n_sim, T, state_dim),"f_observations": sampled observations, shape(n_sim, T, obs_dim).
In filter-rollout mode (predict_times with filtered posteriors), additional
keys "f_predicted_states", "f_predicted_times", and
"f_predicted_observations" are recorded. Segment-level rollouts also
register realized anchor-state sites such as "f_1_x_0" when applicable.
Under numpyro.infer.Predictive(model, num_samples=N), NumPyro prepends a leading
num_samples axis, giving final shapes (num_samples, n_sim, T, dim).
Use dynestyx.flatten_draws to collapse the (num_samples, n_sim) prefix into one
axis for plotting or downstream analysis.
If predict_times is omitted, no public simulator rollout is performed and
these sites are not added.
Simulators¶
Simulation backends and simulator handlers.
DiscreteTimeSimulator
¶
Bases: BaseSimulator
Generate trajectories from a discrete-time dynamical model.
For prediction times \(t_0,\ldots,t_{T-1}\), this simulator draws
n_simulations independent paths according to
The first state in the returned path is the initial-condition draw at
predict_times[0]; the simulator then makes one transition draw for each
adjacent pair of prediction times and samples one observation conditional
on every realized state. See
DiscreteTimeStateEvolution
for how a discrete transition model is represented in a DynamicalModel.
Use DiscreteTimeSimulator as a context manager around a model containing
dsx.sample(name, dynamics, predict_times=...). The active NumPyro seed
supplies randomness, while the realized paths are attached to the trace as
deterministic sites. Use dsx.simulate for
standalone pure-JAX generation without a NumPyro trace.
Examples:
>>> def model(predict_times=None):
... dynamics = DynamicalModel(
... initial_condition=initial_dist,
... state_evolution=transition,
... observation_model=observation,
... )
... dsx.sample("f", dynamics, predict_times=predict_times)
>>> with DiscreteTimeSimulator(n_simulations=3):
... predictive = Predictive(
... model, num_samples=10, exclude_deterministic=False
... )
... draws = predictive(
... jr.PRNGKey(0), predict_times=jnp.arange(20.0)
... )
>>> draws["f_states"].shape
(10, 3, 20, state_dim)
For one direct, pure-JAX model execution:
>>> result = dsx.simulate(
... dynamics,
... rng_key=jr.PRNGKey(0),
... predict_times=jnp.arange(20.0),
... n_simulations=3,
... )
What this does¶
The transition distribution is evaluated with the current state, current control, and the two adjacent time values. Prediction times therefore need not be uniformly spaced, provided the model's transition accepts those intervals.
If controls are supplied, ctrl_times must contain every prediction time
exactly. ctrl_values[k] is used for the transition beginning at \(t_k\)
and for the observation at \(t_k\). The paired control arrays are validated
before simulation.
This handler is generation-only and does not condition on obs_times or
obs_values. Use
LatentPathBuilder
for explicit latent-path inference, or use
Filter or
Smoother for marginalized
inference. Placing this simulator outside a compatible Filter or
Smoother draws posterior rollouts at predict_times.
Configuration and defaults¶
Discrete simulation has no solver configuration: transitions are sampled
directly from dynamics.state_evolution. n_simulations defaults to one
and must be at least one. The simulation dimension is retained even when it
has length one.
NumPyro trace¶
For a raw rollout from dsx.sample("f", ...), the following
numpyro.deterministic sites are added:
"f_x_0": initial states, shape(*plate_shape, n_simulations, state_dim);"f_times": prediction times, shape(*plate_shape, n_simulations, T);"f_states": latent states, shape(*plate_shape, n_simulations, T, state_dim);"f_observations": sampled observations, shape(*plate_shape, n_simulations, T, observation_dim).
Here "f" is replaced by the name passed to dsx.sample. Under
Predictive(..., num_samples=N), NumPyro prepends an N axis to each
shape. Because these sites are deterministic, pass
exclude_deterministic=False to Predictive (or request the site names
explicitly) to include them in its returned dictionary.
When this simulator wraps a Filter or Smoother, the inner handler
records its own configured sites and the simulator's aggregate rollout
sites are instead "f_predicted_times", "f_predicted_states", and
"f_predicted_observations", with the corresponding time, state, and
observation shapes above. Each nonempty prediction segment also records
the state from which that segment starts, with shape
(n_simulations, state_dim): "f_0_x_0" for a segment before the first
posterior time, and "f_{j+1}_x_0" for a segment initialized from the
posterior at inference-time index j. Only segments containing at least
one requested prediction time are recorded. Inside dsx.plate, the segment
name also identifies the plate member, for example "f_p0_1_x_0".
If predict_times is omitted, no simulator rollout or simulator trace
sites are produced. Direct calls to
DiscreteTimeSimulator().simulate
return SimulatedResult without adding NumPyro sites.
Notes
- Use
Simulatorinstead when automatic selection among discrete, ODE, and SDE backends is desirable. DiscreteTimeSimulator().simulate(...)consumes an already allocated simulation key. The public dsx.simulate function splits its root key before dispatch.
Attributes:
| Name | Type | Description |
|---|---|---|
n_simulations |
int
|
Number of independent trajectories drawn per model execution. Defaults to one and must be greater than or equal to one. |
_simulate_forward_from_initial_state(dynamics: DynamicalModel, *, initial_state: Real[Array, 'n_simulations state_dim'] | Real[Array, ' n_simulations'], rng_key: PRNGKeyArray, times: Real[Array, ' time'], ctrl_values: Real[Array, 'time control_dim'] | Real[Array, ' time'] | None) -> SimulatedResult
¶
Run pure forward simulation for a discrete-time model.
simulate(dynamics: DynamicalModel, *, rng_key: PRNGKeyArray, ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None, predict_times: Real[Array, ' predict_time'] | None = None, **kwargs) -> SimulatedResult
¶
Run pure-JAX forward simulation for a discrete-time model.
Unlike dsx.simulate, rng_key is consumed
directly as an already-allocated simulation key and is not pre-split.
Therefore, dsx.simulate(..., rng_key=root_key) is equivalent to
DiscreteTimeSimulator().simulate(..., rng_key=split_key), where
split_key = jax.random.split(root_key)[1].
ODESimulator
¶
Bases: BaseSimulator
Generate trajectories from deterministic continuous-time dynamics.
For an initial-condition distribution \(p_0\), drift function \(f\), and
observation model \(p(y\mid x,u,t)\), ODESimulator draws
n_simulations independent initial states and computes
The ODE solution is evaluated at every value in predict_times. Conditional
on the initial state and controls, the state path is deterministic; the
initial-condition and observation distributions may still make the complete
simulation stochastic. See
ContinuousTimeStateEvolution
for how an ODE is represented in a DynamicalModel by specifying its drift
without a diffusion.
Use ODESimulator as a context manager around a model containing
dsx.sample(name, dynamics, predict_times=...). The active NumPyro seed
supplies randomness, and the computed arrays are then attached to the trace
as deterministic sites. Pass an
ODESimulatorConfig
to choose the Diffrax solver, step-size controller, adjoint, step size, and
step limit. Use dsx.simulate for standalone
pure-JAX generation without a NumPyro trace.
Examples:
Prior-predictive ODE trajectories:
>>> def model(predict_times=None):
... dynamics = DynamicalModel(
... initial_condition=initial_dist,
... state_evolution=ContinuousTimeStateEvolution(
... drift=lambda x, u, t: -rate * x,
... ),
... observation_model=observation,
... )
... dsx.sample("f", dynamics, predict_times=predict_times)
>>> config = ODESimulatorConfig(dt0=1e-2)
>>> with ODESimulator(config, n_simulations=3):
... predictive = Predictive(
... model, num_samples=10, exclude_deterministic=False
... )
... draws = predictive(
... jr.PRNGKey(0), predict_times=jnp.linspace(0.0, 5.0, 51)
... )
>>> draws["f_states"].shape
(10, 3, 51, state_dim)
Standalone pure-JAX simulation uses the same ODE solver:
>>> result = dsx.simulate(
... dynamics,
... rng_key=jr.PRNGKey(0),
... predict_times=times,
... n_simulations=3,
... simulator_config=ODESimulatorConfig(dt0=1e-2),
... )
What this does¶
Each initial-condition draw is integrated independently with Diffrax. The
integration starts at dynamics.t0 when it is defined and otherwise at the
first prediction time. The solved state is saved only at predict_times,
after which the observation model is sampled independently at those states.
If controls are supplied, they form a right-continuous rectilinear path:
the control at a knot ctrl_times[k] is ctrl_values[k], and that value is
held until the next knot.
This handler is generation-only and does not condition on obs_times or
obs_values. Use
LatentPathBuilder
for explicit latent-path inference, or use
Filter or
Smoother for marginalized
inference. Placing this simulator outside a compatible continuous-time
Filter or Smoother draws posterior rollouts at predict_times.
Configuration and defaults¶
ODEs are solved using Diffrax, and settings are controlled by
ODESimulatorConfig.
Its default settings are diffrax.Tsit5(),
diffrax.ConstantStepSize(), diffrax.RecursiveCheckpointAdjoint(),
dt0=1e-3, and max_steps=100_000. Pass different settings when the model
requires them. If simulator_config=None, a default
ODESimulatorConfig() is created.
n_simulations defaults to one and must be at least one. The simulation
dimension is retained even when it has length one.
NumPyro trace¶
For a raw rollout from dsx.sample("f", ...), the following
numpyro.deterministic sites are added:
"f_x_0": initial states, shape(*plate_shape, n_simulations, state_dim);"f_times": prediction times, shape(*plate_shape, n_simulations, T);"f_states": solved states, shape(*plate_shape, n_simulations, T, state_dim);"f_observations": sampled observations, shape(*plate_shape, n_simulations, T, observation_dim).
Here "f" is replaced by the name passed to dsx.sample. Under
Predictive(..., num_samples=N), NumPyro prepends an N axis to each
shape. Because these sites are deterministic, pass
exclude_deterministic=False to Predictive (or request the site names
explicitly) to include them in its returned dictionary.
When this simulator wraps a Filter or Smoother, the inner handler
records its own configured sites and the simulator's aggregate rollout
sites are instead "f_predicted_times", "f_predicted_states", and
"f_predicted_observations", with the corresponding time, state, and
observation shapes above. Each nonempty prediction segment also records
the state from which that segment starts, with shape
(n_simulations, state_dim): "f_0_x_0" for a segment before the first
posterior time, and "f_{j+1}_x_0" for a segment initialized from the
posterior at inference-time index j. Only segments containing at least
one requested prediction time are recorded. Inside dsx.plate, the segment
name also identifies the plate member, for example "f_p0_1_x_0".
If predict_times is omitted, no simulator rollout or simulator trace
sites are produced. Direct calls to
ODESimulator().simulate
return SimulatedResult without adding NumPyro sites.
Notes
- Use
Simulatorinstead when automatic selection among discrete, ODE, and SDE backends is desirable. ODESimulator().simulate(...)consumes an already allocated simulation key. The public dsx.simulate function splits its root key before dispatch.
Attributes:
| Name | Type | Description |
|---|---|---|
simulator_config |
ODE solver and integration settings. Defaults to
|
|
n_simulations |
int
|
Number of independent initial states and trajectories drawn per model execution. Defaults to one and must be greater than or equal to one. |
diffeqsolve_settings |
Normalized settings passed to Diffrax. |
__init__(simulator_config: ODESimulatorConfig | None = None, *, n_simulations: int = 1) -> None
¶
Configure ODE integration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulator_config
|
ODESimulatorConfig | None
|
Structured simulator settings. Defaults to
|
None
|
n_simulations
|
int
|
Number of independent trajectories to simulate. State
and observation paths have shape |
1
|
_simulate_forward_from_initial_state(dynamics: DynamicalModel, *, initial_state: Real[Array, 'n_simulations state_dim'] | Real[Array, ' n_simulations'], rng_key: PRNGKeyArray, times: Real[Array, ' time'], ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None) -> SimulatedResult
¶
Run pure forward simulation for a deterministic continuous-time model.
simulate(dynamics: DynamicalModel, *, rng_key: PRNGKeyArray, ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None, predict_times: Real[Array, ' predict_time'] | None = None, **kwargs) -> SimulatedResult
¶
Run pure-JAX forward simulation for deterministic continuous-time models.
Unlike dsx.simulate, rng_key is consumed
directly as an already-allocated simulation key and is not pre-split.
Therefore, dsx.simulate(..., rng_key=root_key) is equivalent to
ODESimulator().simulate(..., rng_key=jax.random.split(root_key)[1]).
SDESimulator
¶
Bases: BaseSimulator
Generate trajectories from stochastic continuous-time dynamics.
For an initial-condition distribution \(p(x_0)\), drift \(f\), diffusion
coefficient \(g\), and observation model \(p(y\mid x,u,t)\),
SDESimulator draws n_simulations independent paths satisfying
The numerical SDE solution is evaluated at every value in
predict_times. Initial states, Brownian paths, and observations are drawn
independently across the simulation dimension. See
ContinuousTimeStateEvolution
for how an SDE is represented in a DynamicalModel.
Use SDESimulator as a context manager around a model containing
dsx.sample(name, dynamics, predict_times=...). The active NumPyro seed
supplies randomness, and the realized paths are then attached to the trace
as deterministic sites. Pass an
SDESimulatorConfig
to choose the SDE backend, solver, step-size controller, adjoint, step size,
and Brownian-tree tolerance. Use
dsx.simulate for standalone pure-JAX generation
without a NumPyro trace.
Examples:
Fast fixed-step Euler--Maruyama simulation:
>>> def model(predict_times=None):
... dynamics = DynamicalModel(
... initial_condition=initial_dist,
... state_evolution=ContinuousTimeStateEvolution(
... drift=drift,
... diffusion=diffusion,
... ),
... observation_model=observation,
... )
... dsx.sample("f", dynamics, predict_times=predict_times)
>>> config = SDESimulatorConfig(source="em_scan", dt0=1e-3)
>>> with SDESimulator(config, n_simulations=4):
... predictive = Predictive(
... model, num_samples=10, exclude_deterministic=False
... )
... draws = predictive(
... jr.PRNGKey(0), predict_times=jnp.linspace(0.0, 5.0, 51)
... )
>>> draws["f_states"].shape
(10, 4, 51, state_dim)
Use the Diffrax backend when a different SDE solver or step-size controller is required:
>>> config = SDESimulatorConfig(
... source="diffrax",
... solver=diffrax.EulerHeun(),
... dt0=1e-3,
... )
What this does¶
Each initial-condition draw is integrated with an independent Brownian
path. Integration starts at dynamics.t0 when it is defined and otherwise
at the first prediction time. After the state paths are solved, the
observation model is sampled independently at the requested times.
If controls are supplied, they form a right-continuous rectilinear path:
the control at a knot ctrl_times[k] is ctrl_values[k], and that value is
held until the next knot.
This handler is generation-only and does not condition on obs_times or
obs_values. Native SDE latent-path inference should go through a
Discretizer and
LatentPathBuilder;
use
Filter or
Smoother for marginalized
inference. Placing this simulator outside a compatible continuous-time
Filter or Smoother draws posterior rollouts at predict_times.
Configurations and defaults¶
SDESimulatorConfig selects one of two backends:
source="em_scan"uses a fixed-step Euler--Maruyamajax.lax.scan. It is the default and usesdt0=1e-4.source="diffrax"uses the configured Diffrax solver, step-size controller, adjoint, and virtual Brownian tree. Defaults includediffrax.Heun(),diffrax.ConstantStepSize(),diffrax.RecursiveCheckpointAdjoint(), anddt0=1e-4.tol_vbt=Noneresolves todt0 / 2.
Solver choice determines the stochastic integral represented by the numerical solution. In particular, the default Diffrax Heun solver converges to a Stratonovich solution, while Euler--Maruyama converges to an Itô solution. This distinction matters for state-dependent diffusion.
If simulator_config=None, a default SDESimulatorConfig() is created.
n_simulations defaults to one and must be at least one. The simulation
dimension is retained even when it has length one.
NumPyro trace¶
For a raw rollout from dsx.sample("f", ...), the following
numpyro.deterministic sites are added:
"f_x_0": initial states, shape(*plate_shape, n_simulations, state_dim);"f_times": prediction times, shape(*plate_shape, n_simulations, T);"f_states": solved states, shape(*plate_shape, n_simulations, T, state_dim);"f_observations": sampled observations, shape(*plate_shape, n_simulations, T, observation_dim).
Here "f" is replaced by the name passed to dsx.sample. Under
Predictive(..., num_samples=N), NumPyro prepends an N axis to each
shape. Because these sites are deterministic, pass
exclude_deterministic=False to Predictive (or request the site names
explicitly) to include them in its returned dictionary.
When this simulator wraps a Filter or Smoother, the inner handler
records its own configured sites and the simulator's aggregate rollout
sites are instead "f_predicted_times", "f_predicted_states", and
"f_predicted_observations", with the corresponding time, state, and
observation shapes above. Each nonempty prediction segment also records
the state from which that segment starts, with shape
(n_simulations, state_dim): "f_0_x_0" for a segment before the first
posterior time, and "f_{j+1}_x_0" for a segment initialized from the
posterior at inference-time index j. Only segments containing at least
one requested prediction time are recorded. Inside dsx.plate, the segment
name also identifies the plate member, for example "f_p0_1_x_0".
If predict_times is omitted, no simulator rollout or simulator trace
sites are produced. Direct calls to
SDESimulator().simulate
return SimulatedResult without adding NumPyro sites.
Notes
- Use
Simulatorinstead when automatic selection among discrete, ODE, and SDE backends is desirable. SDESimulator().simulate(...)consumes an already allocated simulation key. The public dsx.simulate function splits its root key before dispatch.
Attributes:
| Name | Type | Description |
|---|---|---|
simulator_config |
SDE backend and integration settings. Defaults to
|
|
n_simulations |
int
|
Number of independent initial states, Brownian paths, and trajectories drawn per model execution. Defaults to one and must be greater than or equal to one. |
source |
Active SDE backend, either |
|
diffeqsolve_settings |
Normalized Diffrax-style solver settings. |
|
tol_vbt |
Resolved virtual-Brownian-tree tolerance for the Diffrax
backend, or |
__init__(simulator_config: SDESimulatorConfig | None = None, *, n_simulations: int = 1) -> None
¶
Configure SDE integration settings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
simulator_config
|
SDESimulatorConfig | None
|
Structured simulator settings. Defaults to
|
None
|
n_simulations
|
int
|
Number of independent trajectories to simulate. State
and observation paths have shape |
1
|
_simulate_forward_from_initial_state(dynamics: DynamicalModel, *, initial_state: Real[Array, 'n_simulations state_dim'] | Real[Array, ' n_simulations'], rng_key: PRNGKeyArray, times: Real[Array, ' time'], ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None) -> SimulatedResult
¶
Run pure forward SDE simulation from provided initial states.
simulate(dynamics: DynamicalModel, *, rng_key: PRNGKeyArray, ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None, predict_times: Real[Array, ' predict_time'] | None = None, **kwargs) -> SimulatedResult
¶
Run pure-JAX forward simulation for stochastic continuous-time models.
Unlike dsx.simulate, rng_key is consumed
directly as an already-allocated simulation key and is not pre-split.
Therefore, dsx.simulate(..., rng_key=root_key) is equivalent to
SDESimulator().simulate(..., rng_key=jax.random.split(root_key)[1]).
Simulator
¶
Bases: BaseSimulator
Generate trajectories using the simulator appropriate for the model.
Simulator is the auto-routing simulator handler. Given prediction times
\(t_{0:T-1}\), it draws n_simulations independent trajectories from the
dynamical model:
For continuous-time models, the middle term denotes a numerical ODE or SDE
solve evaluated at predict_times. For discrete-time models, it denotes
successive draws from the transition distribution between adjacent
prediction times.
Use Simulator as a context manager around model execution when the model
contains dsx.sample(name, dynamics, predict_times=...). The active NumPyro
seed supplies randomness; the rollout itself is computed with pure JAX and
its realized arrays are then attached to the NumPyro trace. For
continuous-time models, pass an
ODESimulatorConfig
or
SDESimulatorConfig
as simulator_config to control how the differential equation is solved.
Use dsx.simulate instead when no NumPyro trace is
needed and an explicit rng_key is more convenient.
Examples:
Prior-predictive trajectories with automatic backend selection:
>>> def model(predict_times=None):
... dynamics = DynamicalModel(...)
... dsx.sample("f", dynamics, predict_times=predict_times)
>>> with Simulator(n_simulations=4):
... predictive = Predictive(
... model, num_samples=20, exclude_deterministic=False
... )
... draws = predictive(jr.PRNGKey(0), predict_times=times)
>>> draws["f_states"].shape
(20, 4, T, state_dim)
Pure-JAX forward simulation returns the same trajectory fields without adding NumPyro sites:
>>> result = dsx.simulate(
... dynamics,
... rng_key=jr.PRNGKey(0),
... predict_times=times,
... n_simulations=4,
... )
For posterior state rollouts, place the simulator outside the inference
handler and retain deterministic sites from Predictive:
>>> with Simulator(n_simulations=100):
... with Filter():
... predictive = Predictive(
... model,
... posterior_samples=posterior_samples,
... exclude_deterministic=False,
... )
... forecast = predictive(
... jr.PRNGKey(1),
... obs_times=obs_times,
... obs_values=obs_values,
... predict_times=forecast_times,
... )
What this does¶
The concrete backend is selected from dynamics.state_evolution:
StochasticContinuousTimeStateEvolutionuses SDESimulator.DeterministicContinuousTimeStateEvolutionuses ODESimulator.- Discrete-time state evolution uses DiscreteTimeSimulator.
A simulator is generation-only: raw simulator calls accept
predict_times, not obs_times or obs_values. For observation-conditioned
inference, use
LatentPathBuilder,
Filter, or
Smoother. A simulator may wrap a
Filter or Smoother to draw posterior rollouts at predict_times; in
that composition, the inference handler consumes the observations and
passes posterior state distributions outward to the simulator.
Configurations and defaults¶
simulator_config accepts a
SimulatorConfig and
forwards it to the selected continuous-time backend:
- stochastic continuous-time models accept
SDESimulatorConfig; - deterministic continuous-time models accept
ODESimulatorConfig; - discrete-time models do not accept a simulator config.
If simulator_config=None, discrete transitions are sampled directly,
deterministic continuous-time models use the ODESimulatorConfig defaults
(diffrax.Tsit5() with fixed dt0=1e-3), and stochastic continuous-time
models use the SDESimulatorConfig defaults (the fixed-step "em_scan"
Euler--Maruyama backend with dt0=1e-4). n_simulations defaults to one
and must be at least one. The simulation dimension is retained even when it
has length one.
NumPyro trace¶
For a raw rollout from dsx.sample("f", ...), the following
numpyro.deterministic sites are added:
"f_x_0": initial states, shape(*plate_shape, n_simulations, state_dim);"f_times": prediction times, shape(*plate_shape, n_simulations, T);"f_states": latent states, shape(*plate_shape, n_simulations, T, state_dim);"f_observations": sampled observations, shape(*plate_shape, n_simulations, T, observation_dim).
Here "f" is replaced by the name passed to dsx.sample, T is the
length of the prediction grid, and plate_shape is absent outside a
dsx.plate. Under Predictive(..., num_samples=N), NumPyro prepends an
N axis to every shape above. Because these sites are deterministic, pass
exclude_deterministic=False to Predictive (or request the site names
explicitly) to include them in its returned dictionary.
When the simulator wraps a Filter or Smoother, the inner handler records
its own configured sites and the simulator's aggregate rollout sites are
instead "f_predicted_times", "f_predicted_states", and
"f_predicted_observations", with the corresponding time, state, and
observation shapes above. Each nonempty prediction segment also records
the state from which that segment starts, with shape
(n_simulations, state_dim): "f_0_x_0" for a segment before the first
posterior time, and "f_{j+1}_x_0" for a segment initialized from the
posterior at inference-time index j. Only segments containing at least
one requested prediction time are recorded. Inside dsx.plate, the segment
name also identifies the plate member, for example "f_p0_1_x_0".
If predict_times is omitted, the simulator performs no rollout and adds
no simulator sites. Direct calls to
Simulator().simulate and
dsx.simulate return a SimulatedResult and do not
add NumPyro sites.
Notes
- Controls are passed through to the selected backend. See the concrete simulator for its control interpolation or alignment rules.
- A
Simulatorinstance selects and caches its concrete backend on the first dynamics object it handles. Use separate instances for models requiring different backend types. Simulator().simulate(...)consumes an already allocated simulation key. The public dsx.simulate function splits its root key before dispatch.
Attributes:
| Name | Type | Description |
|---|---|---|
simulator_config |
Optional ODE or SDE solver configuration. Its type must match the model selected at first use. |
|
n_simulations |
int
|
Number of independent trajectories drawn per model execution. Defaults to one and must be greater than or equal to one. |
simulator |
BaseSimulator | None
|
Concrete auto-selected simulator cached on first use. |
_ensure_simulator(dynamics: DynamicalModel) -> BaseSimulator
¶
Instantiate and cache the concrete simulator for dynamics.
simulate(dynamics: DynamicalModel, *, rng_key: PRNGKeyArray, ctrl_times: Real[Array, ' ctrl_time'] | None = None, ctrl_values: Real[Array, 'ctrl_time control_dim'] | Real[Array, ' ctrl_time'] | None = None, predict_times: Real[Array, ' predict_time'] | None = None, **kwargs) -> SimulatedResult
¶
Auto-route to the appropriate pure-JAX simulator backend.
Unlike dsx.simulate, rng_key is consumed
directly as an already-allocated simulation key and is not pre-split.
Therefore, dsx.simulate(..., rng_key=root_key) is equivalent to
Simulator().simulate(..., rng_key=jax.random.split(root_key)[1]).
_slice_tree_for_plate_member(tree, plate_shapes: tuple[int, ...], plate_idx: PlateIndex)
¶
Select one plate member from every matching leaf in a pytree.
Member-specific array leaves are indexed by plate_idx. Classification
uses both shape and the leaf's location in the tree so shared vectors are
not sliced only because their length matches a plate size. For a
constant-coefficient Diffusion, only the coefficient is sliced. Shared
leaves are returned unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tree
|
Pytree whose leaves may contain leading plate dimensions. |
required | |
plate_shapes
|
tuple[int, ...]
|
Sizes of the leading plate dimensions. |
required |
plate_idx
|
PlateIndex
|
One index for each plate dimension. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
PyTree |
Copy of |