tinydiffeq 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/PKG-INFO +41 -8
  2. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/README.md +38 -6
  3. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/docs/api.md +6 -0
  4. tinydiffeq-0.3.0/docs/dae.md +120 -0
  5. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/docs/index.md +8 -5
  6. tinydiffeq-0.3.0/docs/llms.txt +12 -0
  7. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/mkdocs.yml +1 -0
  8. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/pyproject.toml +3 -2
  9. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/__init__.py +11 -5
  10. tinydiffeq-0.3.0/src/tinydiffeq/dae.py +453 -0
  11. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/solution.py +21 -0
  12. tinydiffeq-0.3.0/tests/test_dae.py +406 -0
  13. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_float64_subprocess.py +85 -0
  14. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_gpu.py +37 -1
  15. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/uv.lock +18 -2
  16. tinydiffeq-0.2.0/docs/llms.txt +0 -11
  17. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.githooks/pre-commit +0 -0
  18. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.github/workflows/ci.yml +0 -0
  19. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.github/workflows/docs.yml +0 -0
  20. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.github/workflows/publish.yml +0 -0
  21. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.gitignore +0 -0
  22. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/.python-version +0 -0
  23. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/LICENSE +0 -0
  24. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/docs/adaptive_ad.md +0 -0
  25. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/docs/sde.md +0 -0
  26. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/docs/static_shapes.md +0 -0
  27. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/controllers.py +0 -0
  28. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/interpolation.py +0 -0
  29. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/ode.py +0 -0
  30. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/quadrature.py +0 -0
  31. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/save_at.py +0 -0
  32. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/sde.py +0 -0
  33. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/src/tinydiffeq/solvers.py +0 -0
  34. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/conftest.py +0 -0
  35. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/kernels_reference.py +0 -0
  36. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_ad.py +0 -0
  37. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_adaptive.py +0 -0
  38. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_quadrature.py +0 -0
  39. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_recompile.py +0 -0
  40. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_save_at.py +0 -0
  41. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_sde.py +0 -0
  42. {tinydiffeq-0.2.0 → tinydiffeq-0.3.0}/tests/test_solvers_fixed.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tinydiffeq
3
- Version: 0.2.0
4
- Summary: Tiny differentiable ODE/SDE solvers for JAX bounded-scan adaptive stepping, static shapes, jvp/vjp-safe
3
+ Version: 0.3.0
4
+ Summary: Tiny differentiable ODE/SDE/DAE solvers for JAX with static shapes and composable AD
5
5
  Project-URL: Homepage, https://github.com/HighDimensionalEconLab/tinydiffeq
6
6
  Project-URL: Repository, https://github.com/HighDimensionalEconLab/tinydiffeq
7
7
  Project-URL: Documentation, https://highdimensionaleconlab.github.io/tinydiffeq/
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.13
18
18
  Classifier: Topic :: Scientific/Engineering :: Mathematics
19
19
  Requires-Python: >=3.11
20
20
  Requires-Dist: jax>=0.7.0
21
+ Requires-Dist: nlls-gram>=1.6.0
21
22
  Description-Content-Type: text/markdown
22
23
 
23
24
  # tinydiffeq
@@ -29,9 +30,9 @@ Description-Content-Type: text/markdown
29
30
  [![License: MIT](https://img.shields.io/github/license/HighDimensionalEconLab/tinydiffeq)](https://github.com/HighDimensionalEconLab/tinydiffeq/blob/main/LICENSE)
30
31
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
31
32
 
32
- Tiny differentiable ODE/SDE solvers for JAX: fixed-step Euler/RK4, adaptive
33
- Tsit5 with integral or proportional-integral step-size control, and
34
- Euler–Maruyama for Itô SDEs.
33
+ Tiny differentiable ODE/SDE/DAE solvers for JAX: fixed-step Euler/RK4,
34
+ adaptive Tsit5 with integral or proportional-integral step-size control,
35
+ Euler–Maruyama for Itô SDEs, and nonstiff semi-explicit index-1 DAEs.
35
36
  One bounded `lax.scan` of exactly `max_steps` iterations serves fixed and
36
37
  adaptive stepping, so shapes are static, nothing recompiles as tolerances or
37
38
  curvature change, and every solve is differentiable in **both** forward and
@@ -42,9 +43,9 @@ differentiates through a rollout. After a solve reaches its horizon, a
42
43
 
43
44
  This is a deliberately small, jvp/vjp-friendly subset of
44
45
  [diffrax](https://docs.kidger.site/diffrax/). Use diffrax if you need pytree
45
- states, stiff/implicit solvers, full derivative-term PID control, events,
46
- dense output, or checkpointed/backsolve adjoints. tinydiffeq's single runtime
47
- dependency is `jax`.
46
+ states, stiff or fully implicit solvers, higher-index DAEs, full
47
+ derivative-term PID control, events, dense output, or checkpointed/backsolve
48
+ adjoints. The DAE algebraic solve uses `nlls-gram`.
48
49
 
49
50
  ## Install
50
51
 
@@ -107,6 +108,38 @@ padding. Rejected attempts never appear in the returned trajectory.
107
108
  the adaptive controller still chooses its own internal mesh, and cubic
108
109
  Hermite interpolation evaluates the solution at every requested point.
109
110
 
111
+ ## Semi-explicit DAEs
112
+
113
+ For a square index-1 system `dy/dt = f(y, z, t, args, p)` and
114
+ `0 = g(y, z, t, args, p)`:
115
+
116
+ ```python
117
+ from tinydiffeq import IController, Tsit5, solve_semi_explicit_dae
118
+
119
+
120
+ def dae_f(y, z, t, args, p):
121
+ return p * z
122
+
123
+
124
+ def dae_g(y, z):
125
+ return z - y
126
+
127
+
128
+ dae_sol = solve_semi_explicit_dae(
129
+ dae_f, dae_g, Tsit5(), 0.0, 1.0,
130
+ jnp.asarray(1.0), jnp.asarray(0.5),
131
+ p=jnp.asarray(2.0), dt_0=0.1,
132
+ controller=IController(), max_steps=128,
133
+ )
134
+ print(dae_sol.ys, dae_sol.zs)
135
+ ```
136
+
137
+ `z_0` is a guess and is made consistent automatically. Algebraic roots use
138
+ an implicitly differentiated square LM solve, so JVP, VJP, and
139
+ reverse-over-forward propagate with respect to `y`, `t`, and `p`. See the
140
+ [DAE documentation](https://highdimensionaleconlab.github.io/tinydiffeq/dae/)
141
+ for root controls, `SaveAt`, and scope limits.
142
+
110
143
  ## Gradients through the solve
111
144
 
112
145
  ```python
@@ -7,9 +7,9 @@
7
7
  [![License: MIT](https://img.shields.io/github/license/HighDimensionalEconLab/tinydiffeq)](https://github.com/HighDimensionalEconLab/tinydiffeq/blob/main/LICENSE)
8
8
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
9
 
10
- Tiny differentiable ODE/SDE solvers for JAX: fixed-step Euler/RK4, adaptive
11
- Tsit5 with integral or proportional-integral step-size control, and
12
- Euler–Maruyama for Itô SDEs.
10
+ Tiny differentiable ODE/SDE/DAE solvers for JAX: fixed-step Euler/RK4,
11
+ adaptive Tsit5 with integral or proportional-integral step-size control,
12
+ Euler–Maruyama for Itô SDEs, and nonstiff semi-explicit index-1 DAEs.
13
13
  One bounded `lax.scan` of exactly `max_steps` iterations serves fixed and
14
14
  adaptive stepping, so shapes are static, nothing recompiles as tolerances or
15
15
  curvature change, and every solve is differentiable in **both** forward and
@@ -20,9 +20,9 @@ differentiates through a rollout. After a solve reaches its horizon, a
20
20
 
21
21
  This is a deliberately small, jvp/vjp-friendly subset of
22
22
  [diffrax](https://docs.kidger.site/diffrax/). Use diffrax if you need pytree
23
- states, stiff/implicit solvers, full derivative-term PID control, events,
24
- dense output, or checkpointed/backsolve adjoints. tinydiffeq's single runtime
25
- dependency is `jax`.
23
+ states, stiff or fully implicit solvers, higher-index DAEs, full
24
+ derivative-term PID control, events, dense output, or checkpointed/backsolve
25
+ adjoints. The DAE algebraic solve uses `nlls-gram`.
26
26
 
27
27
  ## Install
28
28
 
@@ -85,6 +85,38 @@ padding. Rejected attempts never appear in the returned trajectory.
85
85
  the adaptive controller still chooses its own internal mesh, and cubic
86
86
  Hermite interpolation evaluates the solution at every requested point.
87
87
 
88
+ ## Semi-explicit DAEs
89
+
90
+ For a square index-1 system `dy/dt = f(y, z, t, args, p)` and
91
+ `0 = g(y, z, t, args, p)`:
92
+
93
+ ```python
94
+ from tinydiffeq import IController, Tsit5, solve_semi_explicit_dae
95
+
96
+
97
+ def dae_f(y, z, t, args, p):
98
+ return p * z
99
+
100
+
101
+ def dae_g(y, z):
102
+ return z - y
103
+
104
+
105
+ dae_sol = solve_semi_explicit_dae(
106
+ dae_f, dae_g, Tsit5(), 0.0, 1.0,
107
+ jnp.asarray(1.0), jnp.asarray(0.5),
108
+ p=jnp.asarray(2.0), dt_0=0.1,
109
+ controller=IController(), max_steps=128,
110
+ )
111
+ print(dae_sol.ys, dae_sol.zs)
112
+ ```
113
+
114
+ `z_0` is a guess and is made consistent automatically. Algebraic roots use
115
+ an implicitly differentiated square LM solve, so JVP, VJP, and
116
+ reverse-over-forward propagate with respect to `y`, `t`, and `p`. See the
117
+ [DAE documentation](https://highdimensionaleconlab.github.io/tinydiffeq/dae/)
118
+ for root controls, `SaveAt`, and scope limits.
119
+
88
120
  ## Gradients through the solve
89
121
 
90
122
  ```python
@@ -4,6 +4,8 @@
4
4
 
5
5
  ::: tinydiffeq.solve_ode
6
6
 
7
+ ::: tinydiffeq.solve_semi_explicit_dae
8
+
7
9
  ::: tinydiffeq.solve_sde
8
10
 
9
11
  ## Solvers
@@ -28,6 +30,10 @@
28
30
 
29
31
  ::: tinydiffeq.SaveAt
30
32
 
33
+ ::: tinydiffeq.LMRootSolver
34
+
35
+ ::: tinydiffeq.DAESolution
36
+
31
37
  ::: tinydiffeq.Solution
32
38
 
33
39
  ## Utilities
@@ -0,0 +1,120 @@
1
+ # Semi-Explicit Index-1 DAEs
2
+
3
+ `solve_semi_explicit_dae` integrates nonstiff systems of the form
4
+
5
+ $$
6
+ \dot y = f(y, z, t, \mathrm{args}, p), \qquad
7
+ 0 = g(y, z, t, \mathrm{args}, p),
8
+ $$
9
+
10
+ where the algebraic equation is square and $g_z$ is nonsingular along the
11
+ solution. `y` and `z` are array states. The implementation supports fixed-step
12
+ RK4 and Tsit5 with fixed or adaptive control.
13
+
14
+ The algebraic solve uses
15
+ [`nlls-gram`](https://highdimensionaleconlab.github.io/nlls_gram/square_systems/)'s
16
+ solve-only `SquareLevenbergMarquardt`: an augmented-QR damped step for the
17
+ primal root and a direct implicit Jacobian solve for derivatives.
18
+
19
+ ## Minimal examples
20
+
21
+ Consider
22
+
23
+ $$
24
+ \dot y = pz, \qquad 0=z-y,
25
+ $$
26
+
27
+ whose reduced solution is $y(t)=z(t)=y_0e^{pt}$.
28
+
29
+ ```python
30
+ import jax.numpy as jnp
31
+
32
+ from tinydiffeq import (
33
+ IController,
34
+ RK4,
35
+ Tsit5,
36
+ solve_semi_explicit_dae,
37
+ )
38
+
39
+
40
+ def f(y, z, t, args, p):
41
+ return p * z
42
+
43
+
44
+ def g(y, z):
45
+ return z - y
46
+
47
+
48
+ fixed = solve_semi_explicit_dae(
49
+ f, g, RK4(), 0.0, 1.0,
50
+ jnp.asarray(1.0), jnp.asarray(0.5),
51
+ p=jnp.asarray(2.0), dt_0=0.01, max_steps=100,
52
+ )
53
+
54
+ adaptive = solve_semi_explicit_dae(
55
+ f, g, Tsit5(), 0.0, 1.0,
56
+ jnp.asarray(1.0), jnp.asarray(0.5),
57
+ p=jnp.asarray(2.0), dt_0=0.1,
58
+ controller=IController(), max_steps=128,
59
+ )
60
+ ```
61
+
62
+ `z_0` is a root-finding guess, not an assumed-consistent initial value. Both
63
+ calls first solve `g(y_0, z, t_0, args, p) = 0`, so the `0.5` guess becomes
64
+ the consistent value `1.0`.
65
+
66
+ ## Nonlinear-solve and AD contract
67
+
68
+ The default root configuration is:
69
+
70
+ ```python
71
+ from tinydiffeq import LMRootSolver
72
+
73
+ root_solver = LMRootSolver(
74
+ max_steps=8,
75
+ atol=None, # 1e-6 float32, 1e-10 float64
76
+ init_damping=1e-3,
77
+ )
78
+ ```
79
+
80
+ The outer `max_steps` counts attempted time steps, including adaptive
81
+ rejections. `root_solver.max_steps` separately bounds one algebraic root.
82
+
83
+ Every root passes `(y, t, p)` through nlls-gram's differentiated parameter
84
+ pytree. Thus it differentiates the defining constraint,
85
+
86
+ $$
87
+ \dot z = -g_z^{-1}
88
+ (g_y\dot y + g_t\dot t + g_p\dot p),
89
+ $$
90
+
91
+ rather than differentiating the LM iterations. The warm-start guess has zero
92
+ derivative by design. `args` is fixed data; put every differentiated model
93
+ quantity in `p`. JVP, VJP, `vmap`, and reverse-over-forward compose through
94
+ the complete DAE solve.
95
+
96
+ An adaptive stage-root failure rejects the time-step attempt and asks the
97
+ controller for a smaller step. A fixed-step root failure terminates the solve.
98
+ In either case `sol.ok` is false if the endpoint is not reached with valid
99
+ algebraic states.
100
+
101
+ ## Saving output
102
+
103
+ All `SaveAt` modes are supported:
104
+
105
+ - `SaveAt(t_1=True)` returns the consistent endpoint.
106
+ - `SaveAt(steps=True)` returns the initial point and accepted internal steps
107
+ as a padded `max_steps + 1` buffer with the usual `accepted` mask.
108
+ - `SaveAt(ts=grid)` Hermite-interpolates `y` onto the requested grid and then
109
+ solves `g=0` at each requested time. It never interpolates `z` independently,
110
+ so every returned algebraic state satisfies the constraint.
111
+
112
+ The result is `DAESolution(ts, ys, zs, ok, num_accepted, accepted)`.
113
+
114
+ ## Deliberate limits
115
+
116
+ This is an explicit, nonstiff, semi-explicit index-1 integrator. It does not
117
+ support mass-matrix or fully implicit DAEs, stiff implicit time stepping,
118
+ higher-index constraints, or automatic index reduction. It is an initial-value
119
+ solver: it does not determine unknown initial costates or solve boundary-value
120
+ or saddle-path conditions.
@@ -1,9 +1,9 @@
1
1
  # tinydiffeq
2
2
 
3
- `tinydiffeq` is a deliberately tiny set of differentiable ODE/SDE integrators
4
- for JAX: fixed-step Euler and RK4, adaptive Tsit5 with integral or
5
- proportional-integral step-size control, and fixed-step Euler–Maruyama for
6
- Itô SDEs. Everything runs
3
+ `tinydiffeq` is a deliberately tiny set of differentiable ODE/SDE/DAE
4
+ integrators for JAX: fixed-step Euler and RK4, adaptive Tsit5 with integral or
5
+ proportional-integral step-size control, fixed-step Euler–Maruyama for Itô
6
+ SDEs, and nonstiff semi-explicit index-1 DAEs. Time integration runs
7
7
  inside one bounded `lax.scan` with static shapes, and every solve is
8
8
  differentiable in **both** forward and reverse mode — including
9
9
  reverse-over-forward, the pattern a Levenberg–Marquardt optimizer with
@@ -14,7 +14,7 @@ It is a jvp/vjp-friendly subset of
14
14
  need any of:**
15
15
 
16
16
  - pytree states (tinydiffeq states are single arrays, scalar or vector)
17
- - stiff or implicit solvers
17
+ - stiff or fully implicit solvers, or higher-index DAEs
18
18
  - full derivative-term PID step-size control
19
19
  - events, root-finding, or backward-time integration
20
20
  - dense output / continuous interpolation objects
@@ -61,6 +61,9 @@ The arity is inspected once and the function is wrapped into the canonical
61
61
  four-argument form, so the compiled code is identical for all four. There is
62
62
  no special autonomous code path: an unused `t` is dead-code-eliminated.
63
63
  `drift` and `diffusion` in [`solve_sde`](sde.md) follow the same convention.
64
+ Semi-explicit DAE fields use `(y, z)`, `(y, z, t)`,
65
+ `(y, z, t, args)`, or `(y, z, t, args, p)`; see
66
+ [Semi-Explicit DAEs](dae.md).
64
67
 
65
68
  ## Minimal example
66
69
 
@@ -0,0 +1,12 @@
1
+ # tinydiffeq
2
+
3
+ > Tiny differentiable ODE/SDE/DAE solvers for JAX. Fixed-step (Euler, RK4) and adaptive (Tsit5 + integral or proportional-integral controller) explicit Runge-Kutta inside one bounded lax.scan of exactly max_steps attempted steps: static shapes, one compilation across explicit controller values/curvature/initial conditions, and solves differentiable in both forward and reverse mode (including reverse-over-forward) with O(max_steps) memory. Nonstiff semi-explicit index-1 DAEs use RK4 or Tsit5 plus nlls-gram's square LM algebraic solve, whose custom JVP implicitly differentiates each root with respect to the differential state, time, and parameters. Adaptive-controller defaults are precision-aware: rtol=1e-4, atol=1e-6 for float32 and rtol=1e-7, atol=1e-9 for float64; dt_min is 10*eps*max(1, abs(t_1)) in the time dtype. Expensive solver work is skipped after the horizon via lax.cond while the scan emits its padded tail. Array states only (scalar or vector). SaveAt selects endpoint, cubic-Hermite interpolation onto a requested grid, or a compact accepted-step prefix padded to max_steps + 1. For DAE observation grids, only the differential state is interpolated and the algebraic constraint is re-solved. 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 or fully implicit solvers, higher-index DAEs, full derivative-term PID control, events, dense output, and adjoint methods.
4
+
5
+ ## Docs
6
+
7
+ - [Home — positioning, vector-field signature convention f(x, t, args, p), minimal examples](https://highdimensionaleconlab.github.io/tinydiffeq/)
8
+ - [Static shapes — the bounded-scan design, SaveAt observation grids, compact accepted steps and padding, why nothing recompiles](https://highdimensionaleconlab.github.io/tinydiffeq/static_shapes/)
9
+ - [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/)
10
+ - [Semi-explicit DAEs — index-1 contract, algebraic LM roots, implicit AD, failure behavior, and SaveAt](https://highdimensionaleconlab.github.io/tinydiffeq/dae/)
11
+ - [SDEs — Euler-Maruyama orders, fixed-noise key semantics, shared-path strong-convergence testing, why SaveAt(ts) raises](https://highdimensionaleconlab.github.io/tinydiffeq/sde/)
12
+ - [API reference — ODE/SDE/DAE solves, solvers, controllers, root configuration, SaveAt, solution types, interpolation, and quadrature](https://highdimensionaleconlab.github.io/tinydiffeq/api/)
@@ -45,5 +45,6 @@ nav:
45
45
  - Home: index.md
46
46
  - Static Shapes: static_shapes.md
47
47
  - Adaptive Stepping and AD: adaptive_ad.md
48
+ - Semi-Explicit DAEs: dae.md
48
49
  - SDEs: sde.md
49
50
  - API Reference: api.md
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tinydiffeq"
7
- version = "0.2.0"
8
- description = "Tiny differentiable ODE/SDE solvers for JAX bounded-scan adaptive stepping, static shapes, jvp/vjp-safe"
7
+ version = "0.3.0"
8
+ description = "Tiny differentiable ODE/SDE/DAE solvers for JAX with static shapes and composable AD"
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
@@ -29,6 +29,7 @@ classifiers = [
29
29
  ]
30
30
  dependencies = [
31
31
  "jax>=0.7.0",
32
+ "nlls-gram>=1.6.0",
32
33
  ]
33
34
 
34
35
  [project.urls]
@@ -1,4 +1,4 @@
1
- """Tiny differentiable ODE/SDE solvers for JAX.
1
+ """Tiny differentiable ODE/SDE/DAE solvers for JAX.
2
2
 
3
3
  solve_ode integrates dx/dt = f(x, t, args, p) with fixed-step (Euler, RK4)
4
4
  or adaptive (Tsit5 + IController/PIController) explicit Runge-Kutta methods
@@ -8,22 +8,26 @@ static and solves are differentiable in BOTH forward and reverse mode
8
8
  output: the endpoint, cubic-Hermite interpolation onto a fixed grid, or
9
9
  accepted internal steps with fixed-shape padding. solve_sde is fixed-step
10
10
  Euler-Maruyama with presampled
11
- diagonal noise. States are arrays (scalar or vector); pytree states,
12
- implicit/stiff solvers, full derivative-term PID control, events, dense
13
- output, and adjoint methods are non-goals use diffrax for those.
11
+ diagonal noise. solve_semi_explicit_dae handles nonstiff index-1 systems with
12
+ an implicitly differentiated algebraic root. States are arrays (scalar or
13
+ vector); pytree states, stiff or fully implicit solvers, full derivative-term
14
+ PID control, events, dense output, and adjoint methods are non-goals — use
15
+ diffrax for those.
14
16
  """
15
17
 
16
18
  from tinydiffeq.controllers import ConstantStepSize, IController, PIController
19
+ from tinydiffeq.dae import LMRootSolver, solve_semi_explicit_dae
17
20
  from tinydiffeq.interpolation import hermite_interpolate
18
21
  from tinydiffeq.ode import solve_ode
19
22
  from tinydiffeq.quadrature import cumulative_trapezoid
20
23
  from tinydiffeq.save_at import SaveAt
21
24
  from tinydiffeq.sde import solve_sde
22
- from tinydiffeq.solution import Solution
25
+ from tinydiffeq.solution import DAESolution, Solution
23
26
  from tinydiffeq.solvers import RK4, Euler, EulerMaruyama, Tsit5
24
27
 
25
28
  __all__ = [
26
29
  "solve_ode",
30
+ "solve_semi_explicit_dae",
27
31
  "solve_sde",
28
32
  "Euler",
29
33
  "RK4",
@@ -34,6 +38,8 @@ __all__ = [
34
38
  "PIController",
35
39
  "SaveAt",
36
40
  "Solution",
41
+ "DAESolution",
42
+ "LMRootSolver",
37
43
  "hermite_interpolate",
38
44
  "cumulative_trapezoid",
39
45
  ]