tinydiffeq
Tiny differentiable ODE/SDE solvers for JAX: fixed-step Euler/RK4, adaptive
Tsit5 with integral or proportional-integral step-size control, and
Euler–Maruyama for Itô SDEs.
One bounded lax.scan of exactly max_steps iterations serves fixed and
adaptive stepping, so shapes are static, nothing recompiles as tolerances or
curvature change, and every solve is differentiable in both forward and
reverse mode — including reverse-over-forward, the pattern a
Levenberg–Marquardt optimizer with geodesic acceleration needs when it
differentiates through a rollout. After a solve reaches its horizon, a
lax.cond skips solver and controller work during the padded scan tail.
This is a deliberately small, jvp/vjp-friendly subset of
diffrax. Use diffrax if you need pytree
states, stiff/implicit solvers, full derivative-term PID control, events,
dense output, or checkpointed/backsolve adjoints. tinydiffeq's single runtime
dependency is jax.
Install
uv add tinydiffeq
For GPU use, install the JAX accelerator build that matches your hardware, for example:
uv add tinydiffeq "jax[cuda13]"
Minimal example
The vector field may take (x), (x, t), (x, t, args), or
(x, t, args, p) — always in that order. args is pass-through data (not an
AD target by convention); p holds differentiable parameters (any pytree).
import jax
import jax.numpy as jnp
from tinydiffeq import solve_ode, Tsit5, IController, SaveAt
jax.config.update("jax_enable_x64", True) # your call — the library never sets it
def f(x, t, args, p):
return -p * x
sol = solve_ode(
f, Tsit5(), 0.0, 2.0, jnp.asarray(1.0),
p=jnp.asarray(1.3),
dt_0=0.1,
controller=IController(rtol=1e-8, atol=1e-10),
max_steps=512,
save_at=SaveAt(ts=jnp.linspace(0.0, 2.0, 21)), # fixed output shape,
) # however many steps adapt
print(sol.xs) # states on the grid
print(sol.ok) # reached t_1 within the max_steps budget?
IController() and PIController() choose tolerances from x_0.dtype:
rtol=1e-4, atol=1e-6 for float32 and rtol=1e-7, atol=1e-9 for
float64. Pass explicit values when tolerances are part of your model's
scientific specification. The default dt_min is
10 * finfo(dtype).eps * max(1, abs(t_1)).
max_steps is the total internal attempt budget: accepted steps plus
rejections. It is not normally the number of returned times. Endpoint mode
returns one time/state, SaveAt(ts=...) returns the requested grid, and
SaveAt(steps=True) returns the initial state and accepted internal steps as
a contiguous prefix of max_steps + 1 rows. The remaining rows repeat the
last accepted state by default; sol.accepted distinguishes data from
padding. Rejected attempts never appear in the returned trajectory.
SaveAt(ts=...) also accepts a Python sequence. These are observation times:
the adaptive controller still chooses its own internal mesh, and cubic
Hermite interpolation evaluates the solution at every requested point.
Gradients through the solve
def endpoint(p):
return solve_ode(
f, Tsit5(), 0.0, 2.0, jnp.asarray(1.0), p=p,
dt_0=0.1, controller=IController(rtol=1e-10, atol=1e-12),
max_steps=512,
).xs
jax.grad(endpoint)(jnp.asarray(1.3)) # reverse mode
jax.jvp(endpoint, (jnp.asarray(1.3),), (jnp.asarray(1.0),)) # forward mode
jax.grad(lambda p: jax.jvp(endpoint, (p,), (jnp.asarray(1.0),))[1])(
jnp.asarray(1.3)
) # reverse-over-forward
The step-size controller is wrapped in stop_gradient (accept/reject is
non-differentiable either way, and the error-ratio power blows up at exactly
zero error); the states differentiate fully through the RK stages. See the
docs for the design
contracts: static shapes and SaveAt, AD through adaptive stepping, SDE key
semantics, and the package API.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tinydiffeq-0.2.0.tar.gz.
File metadata
- Download URL: tinydiffeq-0.2.0.tar.gz
- Upload date:
- Size: 99.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f78c5a3af726ab294eef65d2f23848ec324a3fa374d2d9371284aa3f39c0f379
|
|
| MD5 |
5696fc45ccd754384318df4498e16ed4
|
|
| BLAKE2b-256 |
30da2532915f294c66d742e5b2040afa2521bce741d9f9b9699ba1e52e916731
|
Provenance
The following attestation bundles were made for tinydiffeq-0.2.0.tar.gz:
Publisher:
publish.yml on HighDimensionalEconLab/tinydiffeq
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinydiffeq-0.2.0.tar.gz -
Subject digest:
f78c5a3af726ab294eef65d2f23848ec324a3fa374d2d9371284aa3f39c0f379 - Sigstore transparency entry: 2134685013
- Sigstore integration time:
-
Permalink:
HighDimensionalEconLab/tinydiffeq@54cd2307b4cc774c2a33100d1cf57bb837499bc8 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/HighDimensionalEconLab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@54cd2307b4cc774c2a33100d1cf57bb837499bc8 -
Trigger Event:
release
-
Statement type:
File details
Details for the file tinydiffeq-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tinydiffeq-0.2.0-py3-none-any.whl
- Upload date:
- Size: 17.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c65c2ab549b5bf993fdf2334ba6f6b353785d8c2e231ec06b3dd2d720ef5f354
|
|
| MD5 |
eda554a07959c386f099fd3fe0c23bf4
|
|
| BLAKE2b-256 |
680dfcb783b044a37a3b56db14a399204be223f5c57da468487b951cfa550a69
|
Provenance
The following attestation bundles were made for tinydiffeq-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on HighDimensionalEconLab/tinydiffeq
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tinydiffeq-0.2.0-py3-none-any.whl -
Subject digest:
c65c2ab549b5bf993fdf2334ba6f6b353785d8c2e231ec06b3dd2d720ef5f354 - Sigstore transparency entry: 2134685230
- Sigstore integration time:
-
Permalink:
HighDimensionalEconLab/tinydiffeq@54cd2307b4cc774c2a33100d1cf57bb837499bc8 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/HighDimensionalEconLab
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@54cd2307b4cc774c2a33100d1cf57bb837499bc8 -
Trigger Event:
release
-
Statement type: