Skip to content

Commit

Permalink
Move the content of adaptive to ivpsolve because they are always used…
Browse files Browse the repository at this point in the history
… together (#757)

* Rename simulate_terminal... to solve_for_terminal... so all solution routines share a prefix

* Hide the implementation of the Solution

* Rename solution routines to express adaptivity vs fixed steps

* Move content of adaptive.py to ivpsolve.py
  • Loading branch information
pnkraemer authored Jun 13, 2024
1 parent 13a3d5e commit 7581747
Show file tree
Hide file tree
Showing 31 changed files with 483 additions and 490 deletions.
1 change: 0 additions & 1 deletion docs/api_docs/adaptive.md

This file was deleted.

8 changes: 4 additions & 4 deletions docs/benchmarks/hires/run_hires.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import scipy.integrate
import tqdm

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -96,8 +96,8 @@ def param_to_solution(tol):
ts1 = components.correction_ts1()
strategy = components.strategy_filter(ibm, ts1)
solver = solvers.dynamic(strategy)
control = adaptive.control_proportional_integral_clipped()
adaptive_solver = adaptive.adaptive(
control = ivpsolve.control_proportional_integral_clipped()
adaptive_solver = ivpsolve.adaptive(
solver, atol=1e-2 * tol, rtol=tol, control=control
)

Expand All @@ -108,7 +108,7 @@ def param_to_solution(tol):

# Solve
dt0 = ivpsolve.dt0(vf_auto, (u0,))
solution = ivpsolve.simulate_terminal_values(
solution = ivpsolve.solve_adaptive_terminal_values(
vf_probdiffeq, init, t0=t0, t1=t1, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
8 changes: 4 additions & 4 deletions docs/benchmarks/lotkavolterra/run_lotkavolterra.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import scipy.integrate
import tqdm

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -84,8 +84,8 @@ def param_to_solution(tol):
ibm = components.prior_ibm(num_derivatives=num_derivatives)
strategy = components.strategy_filter(ibm, correction())
solver = solvers.mle(strategy)
control = adaptive.control_proportional_integral()
adaptive_solver = adaptive.adaptive(
control = ivpsolve.control_proportional_integral()
adaptive_solver = ivpsolve.adaptive(
solver, atol=1e-2 * tol, rtol=tol, control=control
)

Expand All @@ -97,7 +97,7 @@ def param_to_solution(tol):

# Solve
dt0 = ivpsolve.dt0(vf_auto, (u0,))
solution = ivpsolve.simulate_terminal_values(
solution = ivpsolve.solve_adaptive_terminal_values(
vf_probdiffeq, init, t0=t0, t1=t1, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
8 changes: 4 additions & 4 deletions docs/benchmarks/pleiades/run_pleiades.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import scipy.integrate
import tqdm

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -108,8 +108,8 @@ def param_to_solution(tol):
ts0_or_ts1 = correction_fun(ode_order=2)
strategy = components.strategy_filter(ibm, ts0_or_ts1)
solver = solvers.dynamic(strategy)
control = adaptive.control_proportional_integral()
adaptive_solver = adaptive.adaptive(
control = ivpsolve.control_proportional_integral()
adaptive_solver = ivpsolve.adaptive(
solver, atol=1e-3 * tol, rtol=tol, control=control
)

Expand All @@ -120,7 +120,7 @@ def param_to_solution(tol):

# Solve
dt0 = ivpsolve.dt0(vf_auto, (u0, du0))
solution = ivpsolve.simulate_terminal_values(
solution = ivpsolve.solve_adaptive_terminal_values(
vf_probdiffeq, init, t0=t0, t1=t1, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
8 changes: 4 additions & 4 deletions docs/benchmarks/vanderpol/run_vanderpol.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import scipy.integrate
import tqdm

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -87,8 +87,8 @@ def param_to_solution(tol):
ts0_or_ts1 = components.correction_ts1(ode_order=2)
strategy = components.strategy_filter(ibm, ts0_or_ts1)
solver = solvers.dynamic(strategy)
control = adaptive.control_proportional_integral_clipped()
adaptive_solver = adaptive.adaptive(
control = ivpsolve.control_proportional_integral_clipped()
adaptive_solver = ivpsolve.adaptive(
solver, atol=1e-3 * tol, rtol=tol, control=control
)

Expand All @@ -99,7 +99,7 @@ def param_to_solution(tol):

# Solve
dt0 = ivpsolve.dt0(vf_auto, (u0, du0))
solution = ivpsolve.simulate_terminal_values(
solution = ivpsolve.solve_adaptive_terminal_values(
vf_probdiffeq, init, t0=t0, t1=t1, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
2 changes: 1 addition & 1 deletion docs/dev_docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ From now on, this change log will be used properly.
This means that the behaviour of, e.g., parameter estimation scripts will change slightly.
A related bugfix in computing the whitened residuals implies that the dynamic solver with a ts1() correction and a dense implementation is not exactly equivalent
to tornadox.ReferenceEK1 anymore (because the tornadox-version still has the same error).
* The interpolation behaviour of the MLESolver when called in solve_and_save_at() had a small error, which amplified the output scale unnecessarily between steps.
* The interpolation behaviour of the MLESolver when called in solve_adaptive_save_at() had a small error, which amplified the output scale unnecessarily between steps.
This has been fixed. As a result, the posterior-uncertainty notebook displays more realistic uncertainty estimates in high-order derivatives. Check it out!

## Prior to v0.2.0
Expand Down
6 changes: 3 additions & 3 deletions docs/examples_misc/use_equinox_bounded_while_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import jax
import jax.numpy as jnp

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.backend import control_flow
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
Expand Down Expand Up @@ -69,14 +69,14 @@ def vf(y, *, t): # noqa: ARG001

strategy = components.strategy_fixedpoint(ibm, ts0)
solver = solvers.solver(strategy)
adaptive_solver = adaptive.adaptive(solver)
adaptive_solver = ivpsolve.adaptive(solver)

tcoeffs = autodiff.taylor_mode_scan(lambda y: vf(y, t=t0), (u0,), num=1)
init = solver.initial_condition(tcoeffs, 1.0)

def simulate(init_val):
"""Evaluate the parameter-to-solution function."""
sol = ivpsolve.simulate_terminal_values(
sol = ivpsolve.solve_adaptive_terminal_values(
vf, init_val, t0=t0, t1=t1, dt0=0.1, adaptive_solver=adaptive_solver
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
import matplotlib.pyplot as plt
from diffeqzoo import backend, ivps

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers, stats
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -211,12 +211,12 @@ def solve_adaptive(theta, *, save_at):
ts0 = components.correction_ts0()
strategy = components.strategy_filter(ibm, ts0)
solver = solvers.solver(strategy)
adaptive_solver = adaptive.adaptive(solver)
adaptive_solver = ivpsolve.adaptive(solver)

tcoeffs = autodiff.taylor_mode_scan(lambda y: vf(y, t=t0), (theta,), num=2)
output_scale = 10.0
init = solver.initial_condition(tcoeffs, output_scale)
return ivpsolve.solve_and_save_at(
return ivpsolve.solve_adaptive_save_at(
vf, init, save_at=save_at, adaptive_solver=adaptive_solver, dt0=0.1
)

Expand Down
6 changes: 3 additions & 3 deletions docs/examples_quickstart/easy_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import jax
import jax.numpy as jnp

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -88,7 +88,7 @@ def vf(y, *, t): # noqa: ARG001

strategy = components.strategy_smoother(ibm, ts0)
solver = solvers.solver(strategy)
adaptive_solver = adaptive.adaptive(solver)
adaptive_solver = ivpsolve.adaptive(solver)
# -

# Why so many layers?
Expand Down Expand Up @@ -136,7 +136,7 @@ def vf(y, *, t): # noqa: ARG001

# +
dt0 = ivpsolve.dt0(lambda y: vf(y, t=t0), (u0,)) # or use e.g. dt0=0.1
solution = ivpsolve.solve_and_save_every_step(
solution = ivpsolve.solve_adaptive_save_every_step(
vf, init, t0=t0, t1=t1, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
6 changes: 3 additions & 3 deletions docs/examples_solver_config/conditioning-on-zero-residual.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import matplotlib.pyplot as plt
from diffeqzoo import backend

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers, stats
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -86,12 +86,12 @@ def vector_field(y, t): # noqa: ARG001
slr1 = components.correction_ts1()
ibm = components.prior_ibm(num_derivatives=NUM_DERIVATIVES)
solver = solvers.solver(components.strategy_fixedpoint(ibm, slr1))
adaptive_solver = adaptive.adaptive(solver, atol=1e-1, rtol=1e-2)
adaptive_solver = ivpsolve.adaptive(solver, atol=1e-1, rtol=1e-2)

dt0 = ivpsolve.dt0(lambda y: vector_field(y, t=t0), (u0,))

init = solver.initial_condition(tcoeffs, output_scale=1.0)
sol = ivpsolve.solve_and_save_at(
sol = ivpsolve.solve_adaptive_save_at(
vector_field, init, save_at=ts, dt0=1.0, adaptive_solver=adaptive_solver
)
# posterior = stats.calibrate(sol.posterior, sol.output_scale)
Expand Down
10 changes: 5 additions & 5 deletions docs/examples_solver_config/posterior_uncertainties.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import matplotlib.pyplot as plt
from diffeqzoo import backend, ivps

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers, stats
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -66,7 +66,7 @@ def vf(*ys, t): # noqa: ARG001
ibm = components.prior_ibm(num_derivatives=4)
ts0 = components.correction_ts0()
solver = solvers.mle(components.strategy_filter(ibm, ts0))
adaptive_solver = adaptive.adaptive(solver, atol=1e-2, rtol=1e-2)
adaptive_solver = ivpsolve.adaptive(solver, atol=1e-2, rtol=1e-2)

ts = jnp.linspace(t0, t0 + 2.0, endpoint=True, num=500)

Expand All @@ -75,7 +75,7 @@ def vf(*ys, t): # noqa: ARG001

tcoeffs = autodiff.taylor_mode_scan(lambda y: vf(y, t=t0), (u0,), num=4)
init = solver.initial_condition(tcoeffs, output_scale=1.0)
sol = ivpsolve.solve_and_save_at(
sol = ivpsolve.solve_adaptive_save_at(
vf, init, save_at=ts, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down Expand Up @@ -121,13 +121,13 @@ def vf(*ys, t): # noqa: ARG001
ibm = components.prior_ibm(num_derivatives=4)
ts0 = components.correction_ts0()
solver = solvers.mle(components.strategy_fixedpoint(ibm, ts0))
adaptive_solver = adaptive.adaptive(solver, atol=1e-2, rtol=1e-2)
adaptive_solver = ivpsolve.adaptive(solver, atol=1e-2, rtol=1e-2)

ts = jnp.linspace(t0, t0 + 2.0, endpoint=True, num=500)

# +
init = solver.initial_condition(tcoeffs, output_scale=1.0)
sol = ivpsolve.solve_and_save_at(
sol = ivpsolve.solve_adaptive_save_at(
vf, init, save_at=ts, dt0=dt0, adaptive_solver=adaptive_solver
)

Expand Down
10 changes: 5 additions & 5 deletions docs/examples_solver_config/second_order_problems.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import matplotlib.pyplot as plt
from diffeqzoo import backend, ivps

from probdiffeq import adaptive, ivpsolve
from probdiffeq import ivpsolve
from probdiffeq.impl import impl
from probdiffeq.solvers import components, solvers
from probdiffeq.taylor import autodiff
Expand Down Expand Up @@ -56,14 +56,14 @@ def vf_1(y, t): # noqa: ARG001
ibm = components.prior_ibm(num_derivatives=4)
ts0 = components.correction_ts0()
solver_1st = solvers.mle(components.strategy_filter(ibm, ts0))
adaptive_solver_1st = adaptive.adaptive(solver_1st, atol=1e-5, rtol=1e-5)
adaptive_solver_1st = ivpsolve.adaptive(solver_1st, atol=1e-5, rtol=1e-5)


tcoeffs = autodiff.taylor_mode_scan(lambda y: vf_1(y, t=t0), (u0,), num=4)
init = solver_1st.initial_condition(tcoeffs, output_scale=1.0)
# -

solution = ivpsolve.solve_and_save_every_step(
solution = ivpsolve.solve_adaptive_save_every_step(
vf_1, init, t0=t0, t1=t1, dt0=0.1, adaptive_solver=adaptive_solver_1st
)

Expand All @@ -90,14 +90,14 @@ def vf_2(y, dy, t): # noqa: ARG001
ibm = components.prior_ibm(num_derivatives=4)
ts0 = components.correction_ts0(ode_order=2)
solver_2nd = solvers.mle(components.strategy_filter(ibm, ts0))
adaptive_solver_2nd = adaptive.adaptive(solver_2nd, atol=1e-5, rtol=1e-5)
adaptive_solver_2nd = ivpsolve.adaptive(solver_2nd, atol=1e-5, rtol=1e-5)


tcoeffs = autodiff.taylor_mode_scan(lambda *ys: vf_2(*ys, t=t0), (u0, du0), num=3)
init = solver_2nd.initial_condition(tcoeffs, output_scale=1.0)
# -

solution = ivpsolve.solve_and_save_every_step(
solution = ivpsolve.solve_adaptive_save_every_step(
vf_2, init, t0=t0, t1=t1, dt0=0.1, adaptive_solver=adaptive_solver_2nd
)

Expand Down
4 changes: 2 additions & 2 deletions docs/getting_started/choosing_a_solver.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ If that does not work: let me know what you come up with...
## Filters vs smoothers

Almost always, use a `components.strategy_filter` strategy for `simulate_terminal_values`,
a `components.strategy_smoother` strategy for `solve_and_save_every_step`,
and a `components.strategy_fixedpoint` strategy for `solve_and_save_at`.
a `components.strategy_smoother` strategy for `solve_adaptive_save_every_step`,
and a `components.strategy_fixedpoint` strategy for `solve_adaptive_save_at`.
Use either a filter (if you must) or a smoother (recommended) for `solve_fixed_step`.
Other combinations are possible, but rather rare
(and require some understanding of the underlying statistical concepts).
Expand Down
4 changes: 2 additions & 2 deletions docs/getting_started/transitioning_from_other_packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ ProbDiffEq can reproduce most of the implementations in Tornadox:
| `ek1.ReferenceEK1()` | `dynamic(strategy_filter(ibm_adaptive(), ts1()))` | Combine with `impl.select("dense", ...)` |
| `ek1.ReferenceEK1ConstantDiffusion()` | `mle(strategy_filter(ibm_adaptive(), ts1()))` | Combine with `impl.select("dense", ...)`. |
| `ek1.DiagonalEK1()` | Work in progress. | |
| `solver.solve()` | `solve_and_save_every_step()` | Try `solve_and_save_at()` instead. |
| `solver.solve()` | `solve_adaptive_save_every_step()` | Try `solve_adaptive_save_at()` instead. |
| `solver.simulate_final_state()` | `simulate_terminal_values()` | ProbDiffEq compiles the whole loop; it will be much faster. |
| `solver.solution_generator()` | Work in progress. | |
| `init.TaylorMode()` | `taylor.autodiff.taylor_mode` | Consider `taylor.autodiff.forward_mode_recursive()` for low numbers of derivatives and `taylor.autodiff.taylor_mode_doubling()` for (absurdly) high numbers of derivatives |
Expand Down Expand Up @@ -161,5 +161,5 @@ Most of the divergences from Diffrax apply.
Additionally:

* Solution objects in ProbDiffEq are random processes (posterior distributions). Random variable types replace most vectors and matrices. This statistical description is richer than a point estimate but needs to be calibrated and demands a non-trivial interaction with the solution (e.g. via sampling from it instead of simply plotting the point-estimate)
* ProbDiffEq offers different solution methods: `simulate_terminal_values()`, `solve_and_save_every_step()`, or `solve_and_save_at()`. Many conventional ODE solver suites expose this functionality through flags in a single `solve` function.
* ProbDiffEq offers different solution methods: `simulate_terminal_values()`, `solve_adaptive_save_every_step()`, or `solve_adaptive_save_at()`. Many conventional ODE solver suites expose this functionality through flags in a single `solve` function.
Expressing different modes of solving differential equations in different functions almost exclusively affects the source-code simplicity; but it also allows matching the solver to the solving mode (e.g., terminal values vs save-at). For example, `simulate_terminal_values()` is best combined with a filter.
1 change: 0 additions & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ nav:
- examples_misc/use_equinox_bounded_while_loop.ipynb
- API DOCUMENTATION:
- ivpsolve: api_docs/ivpsolve.md
- adaptive: api_docs/adaptive.md
- impl: api_docs/impl.md
- solvers:
- components: api_docs/solvers/components.md
Expand Down
Loading

0 comments on commit 7581747

Please sign in to comment.