# tinydiffeq

> Tiny differentiable ODE/SDE solvers for JAX. Fixed-step (Euler, RK4) and adaptive (Tsit5 + integral controller) explicit Runge-Kutta inside one bounded lax.scan of exactly max_steps iterations: static shapes, one compilation across tolerances/curvature/initial conditions, and solves differentiable in both forward and reverse mode (including reverse-over-forward) with O(max_steps) memory. Array states only (scalar or vector). SaveAt selects endpoint, cubic-Hermite interpolation onto a fixed grid, or raw padded step rows. solve_sde is fixed-step Euler-Maruyama with presampled diagonal noise keyed on a PRNG key. A deliberately small subset of diffrax: use diffrax for pytree states, stiff/implicit solvers, PID control, events, dense output, and adjoint methods.

## Docs

- [Home — positioning, vector-field signature convention f(x, t, args, p), minimal examples](https://highdimensionaleconlab.github.io/tinydiffeq/)
- [Static shapes — the bounded-scan design, SaveAt as the shape contract, duplicate rows vs inf padding, why nothing recompiles](https://highdimensionaleconlab.github.io/tinydiffeq/static_shapes/)
- [Adaptive stepping and AD — stop-gradiented controller rationale, horizon-clip growth guard, non-differentiable interpolation knots, double-where NaN safety](https://highdimensionaleconlab.github.io/tinydiffeq/adaptive_ad/)
- [SDEs — Euler-Maruyama orders, fixed-noise key semantics, shared-path strong-convergence testing, why SaveAt(ts) raises](https://highdimensionaleconlab.github.io/tinydiffeq/sde/)
- [Migration — exact recipes replacing hand-rolled rk4_grid/tsit5_free loops, poisoning via jnp.where(sol.ok, ...), documented behavior changes](https://highdimensionaleconlab.github.io/tinydiffeq/migration/)
- [API reference — solve_ode, solve_sde, solvers, controllers, SaveAt, Solution, hermite_interpolate, cumulative_trapezoid](https://highdimensionaleconlab.github.io/tinydiffeq/api/)
