Daniel Waxman
Basis Research Institute & MIT
Joint/ongoing work with



Dartmouth ACM Seminar
September 29th, 2026

dynestyx is a probabilistic programming
library for dynamical systems (DW et al., 2026)Waxman, D., Batenkov, D., Feser, J., Zane, A., Bingham, E., Marzouk, Y., & Levine, M. E. (2026). Dynestyx: A probabilistic programming library for dynamical systems (arXiv:2606.16985). arXiv. https://doi.org/10.48550/arXiv.2606.16985
\[ x_t \sim \overbrace{p(x_t \mid x_{t-1})}^{\text{transition density}} \quad\text{or}\quad \mathrm{d}x_t = \overbrace{f(x_t, t)\,\mathrm{d}t}^{\text{drift}} + \overbrace{g(x_t, t)\,\mathrm{d}W_t}^{\text{diffusion}} \]
\[ y_t \sim p(y_t \mid x_t) \]
\[ x_t \sim \overbrace{p_{\textcolor{#2A9DF4}{\theta}}(x_t \mid x_{t-1})}^{\text{transition density}} \quad\text{or}\quad \mathrm{d}x_t = \overbrace{f_{\textcolor{#2A9DF4}{\theta}}(x_t, t)\,\mathrm{d}t}^{\text{drift}} + \overbrace{g_{\textcolor{#2A9DF4}{\theta}}(x_t, t)\,\mathrm{d}W_t}^{\text{diffusion}} \]
\[ y_t \sim p_{\textcolor{#2A9DF4}{\theta}}(y_t \mid x_t) \]

A Probabilistic Programming Language
dynestyx is a library that lets us
perform each of these inference tasks
dynestyx model from the ground updynestyx
model together; remember, we write down the idealized versiondef model(obs_times=None, obs_values=None, predict_times=None):
dynamics = dsx.DynamicalModel(
initial_condition=dist.MultivariateNormal(
jnp.zeros(1), jnp.eye(1)),
state_evolution=dsx.ContinuousTimeStateEvolution(
drift=lambda x, u, t: jnp.sin(x),
diffusion=dsx.ScalarDiffusion(1.0, bm_dim=1),
),
observation_model=dsx.LinearGaussianObservation(
H=jnp.eye(1), R=0.1**2 * jnp.eye(1)),
)
dsx.sample("f", dynamics, obs_times=obs_times,
obs_values=obs_values, predict_times=predict_times)
dynestyx model from the ground updef model(obs_times=None, obs_values=None, predict_times=None):
alpha = numpyro.sample("alpha", dist.HalfNormal(0.5))
dynamics = dsx.DynamicalModel(
initial_condition=dist.MultivariateNormal(
jnp.zeros(1), jnp.eye(1)),
state_evolution=dsx.ContinuousTimeStateEvolution(
drift=lambda x, u, t: alpha * jnp.sin(x),
diffusion=dsx.ScalarDiffusion(1.0, bm_dim=1),
),
observation_model=dsx.LinearGaussianObservation(
H=jnp.eye(1), R=0.1**2 * jnp.eye(1)),
)
dsx.sample("f", dynamics, obs_times=obs_times,
obs_values=obs_values, predict_times=predict_times)
with dsx.Simulator():
sim = Predictive(model, num_samples=1)(key, predict_times=ts)
\[ p(\textcolor{#2A9DF4}{\theta} \mid y_{1:T}) \propto p(\textcolor{#2A9DF4}{\theta})\, p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) \]
← already hard! classical Bayesian inference
\[ p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) = \prod_{k=1}^{T} p(y_k \mid y_{1:k-1}, \textcolor{#2A9DF4}{\theta}) \]
← but where do these come from?
\[ p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) = \int \textcolor{#222222}{p(x_0)} \prod_{k=1}^{T} \textcolor{#00897B}{p_\theta(y_k \mid x_k)}\, \textcolor{#E64B35}{p_\theta(x_k \mid x_{k-1})}\, \mathrm{d}x_{0:T} \]
Duffield, S., Iqbal, S., & Corenflos, A. (2026). cuthbert [Computer software]. https://github.com/state-space-models/cuthbert
Computation via “unrolling”
\[ p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) = \int \textcolor{#222222}{p(x_0)} \prod_{k=1}^{T} \textcolor{#00897B}{p_\theta(y_k \mid x_k)}\, \textcolor{#E64B35}{p_\theta(x_k \mid x_{k-1})}\, \mathrm{d}x_{0:T} \]
dynestyx, we call this a
LatentPathBuilderwith LatentPathBuilder():
with Discretizer(LocalLinearizationConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)

Computation via filtering
\[ p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) = \prod_{k=1}^{T} \int \textcolor{#00897B}{p_\theta(y_k \mid x_k)}\, p_\theta(x_k \mid y_{1:k-1})\, \mathrm{d}x_k \]
with Filter(ContinuousTimeEKFConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)

Andrieu, C., & Roberts, G. O. (2009). The pseudo-marginal approach for efficient Monte Carlo computations. The Annals of Statistics, 37(2), 697–725. https://doi.org/10.1214/07-AOS574
Andrieu, C., Doucet, A., & Holenstein, R. (2010). Particle Markov chain Monte Carlo methods. Journal of the Royal Statistical Society Series B: Statistical Methodology, 72(3), 269–342. https://doi.org/10.1111/j.1467-9868.2009.00736.x
Variational inference
\[ p(y_{1:T} \mid \textcolor{#2A9DF4}{\theta}) = \prod_{k=1}^{T} \int \textcolor{#00897B}{p_\theta(y_k \mid x_k)}\, p_\theta(x_k \mid y_{1:k-1})\, \mathrm{d}x_k \]
with Filter(ContinuousTimeEKFConfig()):
guide = AutoNormal(model)
svi = SVI(model, guide, optax.adam(1e-2),
loss=Trace_ELBO())
result = svi.run(key, 2_000, obs_times, obs_values)

Tran, M.-N., Nott, D. J., & Kohn, R. (2017). Variational Bayes with intractable likelihood. Journal of Computational and Graphical Statistics, 26(4), 873–882. https://doi.org/10.1080/10618600.2017.1330205
To approximate or to not approximate
An example with discretization
Continuous-time EKF
Euler–Maruyama
Local linearization
Ensemble Kalman filter
Variational inference
\(\textcolor{#2A9DF4}{\alpha} \sim \mathrm{HalfNormal}(0.5)\)
\(x_0 \sim \mathcal{N}(0, 1)\)
\(\mathrm{d}x_t = \textcolor{#2A9DF4}{\alpha} \sin(x_t)\,\mathrm{d}t + \mathrm{d}W_t\)
\(y_t \sim \mathcal{N}(x_t, 0.1^2)\)
\(x_{k+1} \mid x_k \sim \mathcal{N}\big(x_k + \textcolor{#2A9DF4}{\alpha} \sin(x_k)\,\Delta t,\; \Delta t\big)\)
\(J_k = \textcolor{#2A9DF4}{\alpha} \cos(x_k)\)
\(x_{k+1} \mid x_k \sim \mathcal{N}\Big(x_k + \tfrac{e^{J_k \Delta t} - 1}{J_k}\, \textcolor{#2A9DF4}{\alpha} \sin(x_k),\; \tfrac{e^{2 J_k \Delta t} - 1}{2 J_k}\Big)\)
with Filter(ContinuousTimeEKFConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)
with Filter(EKFConfig()):
with Discretizer(EulerMaruyamaConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)
with Filter(EKFConfig()):
with Discretizer(LocalLinearizationConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)
with Filter(ContinuousTimeEnKFConfig()):
mcmc = MCMC(NUTS(model), num_warmup=500,
num_samples=1_000)
mcmc.run(key, obs_times, obs_values)
with Filter(ContinuousTimeEnKFConfig()):
guide = AutoNormal(model)
svi = SVI(model, guide, optax.adam(1e-2),
loss=Trace_ELBO())
result = svi.run(key, 300,
obs_times, obs_values)





Can we do X?
Yes.
\(y_t \mid x_t \sim \mathcal{N}\!\left(x_t,\,0.01 I_2\right)\)
layers = sample_layers() # Gaussian priors
dynamics = dsx.ContinuousTimeStateEvolution(
drift=lambda x, u, t: mlp(layers, x),
diffusion=dsx.ScalarDiffusion(0.01, bm_dim=2),
)
Yes, we can do (a Bayesian version of) SINDy (Brunton et al., 2016; Hirsh et al., 2022).
theta = numpyro.sample("theta",
dist.Laplace(0, .1).expand((2, 10)).to_event(2))
dynamics = dsx.ContinuousTimeStateEvolution(
drift=lambda x, u, t: theta @ library(x),
diffusion=dsx.ScalarDiffusion(.01, bm_dim=2))
guide = AutoMultivariateNormal(filtered_model, **guide_init)
Brunton, S. L., Proctor, J. L., & Kutz, J. N. (2016). Discovering governing equations from data by sparse identification of nonlinear dynamical systems. Proceedings of the National Academy of Sciences, 113(15), 3932–3937. https://doi.org/10.1073/pnas.1517384113
Hirsh, S. M., Barajas-Solano, D. A., & Kutz, J. N. (2022). Sparsifying priors for Bayesian uncertainty quantification in model discovery. Royal Society Open Science, 9(2), Article 211823. https://doi.org/10.1098/rsos.211823
Yes... even for PDEs :)
True system
\(\displaystyle \frac{\partial u}{\partial t}=\nu\frac{\partial^2u}{\partial x^2}+1.1u(1-u)\)
\(x\in(0,1),\quad u(t,0)=u(t,1)=0,\quad \nu=0.01\)
Hybrid model
\(\displaystyle \dot{\mathbf u}=\underbrace{\nu L_h\mathbf u}_{\text{known diffusion}}+\underbrace{\color{#00897B}{c_\theta(\mathbf u)}}_{\text{learned reaction}}\)
\(\mathbf y_k\mid\mathbf u(t_k)\sim\mathcal N\!\left(\mathbf u(t_k),\sigma_y^2 I\right)\)
Solin, A., & Särkkä, S. (2020). Hilbert space methods for reduced-rank Gaussian process regression. Statistics and Computing, 30(2), 419–446. https://doi.org/10.1007/s11222-019-09886-w
Yes... even for PDEs :)
c = sample_hsgp() # GP prior for the reaction
dynamics = dsx.ContinuousTimeStateEvolution(
drift=lambda u, _, t: nu * Lh @ u + c(u))
guide = AutoMultivariateNormal(filtered_model, **guide_init)
Yes.
mu0_global = numpyro.sample('mu0_global', dist.Normal(0., .5))
sigma0 = numpyro.sample('sigma0', dist.HalfNormal(.3))
with dsx.plate('individuals', N):
mu = numpyro.sample('mu', dist.Normal(mu_global, sigma))
mu0 = numpyro.sample('mu0', dist.Normal(mu0_global, sigma0))
ou = make_ou(mu, initial_mean=mu0, initial_cov=s0**2)
return dsx.sample('x', ou, **data)



More advanced PDEs (Coming soon...)
Drovandi, C., Everitt, R. G., Golightly, A., & Prangle, D. (2022). Ensemble MCMC: Accelerating pseudo-marginal MCMC for state space models using the ensemble Kalman filter. Bayesian Analysis, 17(1). https://doi.org/10.1214/20-BA1251
Chen, Y., Sanz-Alonso, D., & Willett, R. (2022). Autodifferentiable ensemble Kalman filters. SIAM Journal on Mathematics of Data Science, 4(2), 801–833. https://doi.org/10.1137/21M1434477
| Sampler | Time | ESS / s | max \(\hat{R}\) | E-BFMI |
|---|---|---|---|---|
| particle MCMC | 113 s | 0.16 | 1.17 | – |
| ensemble MCMC | 102 s | 11.8 | 1.01 | – |
| ensemble NUTS | 294 s | 9.4 | 1.00 | 1.02 |
Adrian, M., Sanz-Alonso, D., & Willett, R. (2026). Auto-differentiable data assimilation: Co-learning of states, dynamics, and filtering algorithms (arXiv:2603.20891). arXiv. https://doi.org/10.48550/arXiv.2603.20891
dynestyx gives an API for proper scoring rules, and is
compatible with learning of filters (related to Bach et al., 2026)!Bach, E., Baptista, R., Bröcker, J., Chen, B., & Stuart, A. (2026). Learning probabilistic filters with strictly proper scoring rules (arXiv:2606.26497). arXiv. https://doi.org/10.48550/arXiv.2606.26497
dynestyx provides the state-of-the-art in inference for practitioners to use, helping to close a gap between theory and applicationdynestyx creates implies new algorithms, and an avenue for applying new methodsdanwaxman.github.io
dwaxman@mit.edu
dynestyx

Paper

GitHub