Skip to content

tinydiffeq 2.5.0: SRA1 SDAEs, unroll, vmapped-loop skipping - #10

Merged
jlperla merged 5 commits into
mainfrom
sde-noise-solvers
Aug 5, 2026
Merged

tinydiffeq 2.5.0: SRA1 SDAEs, unroll, vmapped-loop skipping#10
jlperla merged 5 commits into
mainfrom
sde-noise-solvers

Conversation

@jlperla

@jlperla jlperla commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

  • solve_semi_explicit_sdae accepts SRA1 (stage root at t + 3/4 dt plus the endpoint root; noise via solver.sample_noise, matching solve_sde); Milstein stays excluded since its commutativity contract does not survive the implicit reduction.
  • unroll= on solve_sde and fixed-step solve_ode: identical values, fewer GPU dispatches — 2–3× faster reverse mode on L40S NN-policy ensembles.
  • Vmapped loop tails now skip for real: batched-predicate conds lower to both-branches selects, so vmapped adaptive solves used to execute every max_steps slot. Skip conds are now gated on an unvmap_all primitive (scalar predicate under vmap). Measured on L40S: vmapped adaptive Tsit5 primal 12–16× faster at max_steps=1024, grad 1.6–2.6×; per-lane adaptivity, values, and AD unchanged.
  • Removes the max_steps_is_success compat field and the private brownian-increment alias.
  • Docs reorganized around problem pages (Home/ODEs/SDEs/DAEs/SDAEs/Markov/Exponential/API); migration notes dropped; docstrings shortened; README gains the SRA1 explicit-noise example.
  • New GPU benchmark suite (benchmarks/gpu_trajectories.py) driving trained kernels investment policies as the drift, plus exported policy weights and the Vulcan L40S results memo (benchmarks/results/2026-08-04_vulcan-l40s-sde-fixed.md).
  • float32 parametrization for SDE/fixed-step tests; GPU tests for vmapped ensembles with explicit noise and reverse-mode-over-vmap at n ∈ {31, 127}; 340 tests green.

Test plan

  • uv run pytest -q (340 passed), ruff clean, mkdocs build --strict clean
  • tests/test_gpu.py green on Vulcan L40S (24/24)
  • Benchmarks reproducible from committed JSON/markdown under benchmarks/results/

🤖 Generated with Claude Code

https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD

jlperla and others added 5 commits August 4, 2026 17:40
solve_semi_explicit_sdae accepts SRA1: noise comes from
solver.sample_noise, and each step adds a root-restored drift stage at
t + 3/4 dt before the endpoint root. Milstein stays excluded because its
commutativity contract does not survive the implicit reduction.

solve_sde and fixed-step solve_ode gain a static unroll= argument
forwarded to their scans: identical values, fewer GPU dispatches. On
L40S NN-policy ensembles it cut reverse-mode solve time 2-3x.

Removes the max_steps_is_success field (never broadened root
acceptance) and the private brownian-increment alias, shortens the
public docstrings to match the reorganized docs, and adds float32
parametrization plus GPU tests for vmapped ensembles with explicit
noise and reverse-mode over vmap.

Co-Authored-By: Mecha Perla (Claude) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD
Nav is now Home / ODEs / SDEs / DAEs / SDAEs / Markov / Exponential /
API. static_shapes, adaptive_ad, aux, and rodas5p fold into the new
ode.md and the problem pages; migration notes are gone; README gains
the SRA1 explicit-noise example; llms.txt regenerated.

Co-Authored-By: Mecha Perla (Claude) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD
gpu_trajectories.py benchmarks vmapped per-trajectory solves (SRA1/EM/
RK4, primal and jit(grad) w.r.t. x_0/p/noise) with remat, unroll,
matmul-precision, and noise-mode knobs. export_growth_policies.py
trains the kernels growth policies and exports their weights; the
neoclassical and stochastic-growth drifts evaluate those trained MLPs.
Vulcan L40S results and summary under results/.

Co-Authored-By: Mecha Perla (Claude) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD
A batched-predicate lax.cond lowers under vmap to a both-branches
select, so vmapped adaptive solves executed every max_steps attempt
slot regardless of actual work (8-attempt Tsit5 solves at B=32 cost
47.5 ms with max_steps=1024 vs 2.6 ms with 64 on CPU). The skip conds
in the bounded ODE loop (chunk and attempt level), the fixed clipped
scan, both DAE adaptive bodies, and the SDE/SDAE failure tails are now
gated on unvmap_all, a primitive whose batching rule reduces the
predicate over the batch axis, so the tail after the slowest lane
skips for real. Vmapped adaptive primal cost is now budget-invariant
(0.5 ms at every budget, 95x at max_steps=1024); reverse mode improves
3.9x but still stores per-slot scan residuals. Per-lane adaptivity,
values, counters, and AD are unchanged, pinned by
tests/test_vmap_adaptive.py, which also pins the documented
masked-residual pattern for collocation on adaptive output.

Co-Authored-By: Mecha Perla (Claude) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD
Co-Authored-By: Mecha Perla (Claude) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C8SWZyLFpzVjEX5WCRLJnD
@jlperla
jlperla merged commit 841b3e9 into main Aug 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant