Skip to content

Solvers: adaptive steps¤

RejectionLoop(solver: solver_protocols.Solver, clip_dt: bool, error: Any, control: controllers.Control, while_loop: Callable, stop_gradient_through_dt: bool = True) ¤

An implementation of a rejection loop.

clip_dt = clip_dt instance-attribute ¤

control = control instance-attribute ¤

error = error instance-attribute ¤

init(state_solver, dt) -> TimeStepState ¤

Initialise the adaptive solver state.

interp_at_t1(args) ¤

If we stepped exactly to t1, still interpolate.

interp_beyond_t1(args) ¤

If we stepped cleanly over t1, interpolate.

interp_skip(args) ¤

If step_from.t < t1, don't interpolate.

loop(state0: TimeStepState, *, t1, atol, rtol, eps, damp) -> tuple[Any, TimeStepState] ¤

Repeatedly attempt a step until the controller is happy.

Notably: - This function may never attempt a step if the current timestep is beyond t1. - If we step beyond t1, this function interpolates to t1.

solver = solver instance-attribute ¤

step(s_and_t1_and_tols_and_damp) ¤

Do a rejection-loop step.

Keep attempting steps until one is accepted.

step_attempt(state: _RejectionLoopState, *, t1, atol, rtol, damp) -> _RejectionLoopState ¤

Attempt a step.

Perform a step with an IVP solver and propose a future time-step based on tolerances and error estimates.

step_extract_timestep_state(state: _RejectionLoopState) -> TimeStepState ¤

Extract a time-step-state after a successful rejection loop.

step_init_loopstate(s0: TimeStepState) -> _RejectionLoopState ¤

Initialise the rejection state.

stop_gradient_through_dt = stop_gradient_through_dt instance-attribute ¤

while_loop = while_loop instance-attribute ¤

S = TypeVar('S') module-attribute ¤

T = TypeVar('T') module-attribute ¤

TimeStepState ¤

A state variable type for adaptive time-stepping.

control: Any instance-attribute ¤

The controller state.

dt: float instance-attribute ¤

The time-step-size proposal for the next step.

error_step_from: Any instance-attribute ¤

The error-estimate corresponding to 'step_from'.

interp_from: T instance-attribute ¤

Where to continue interpolation from.

This is the left-hand side of the current subinterval.

step_from: T instance-attribute ¤

Where to continue time-stepping from.

This is the right-hand side boundary of the current subinterval.

__all__ = ['RejectionLoop', 'TimeStepState', 'solve_adaptive_save_at', 'solve_adaptive_terminal_values'] module-attribute ¤

solve_adaptive_save_at(*, solver: solver_protocols.Solver, error, control: controllers.Control | None = None, clip_dt: bool = False, while_loop: Callable = flow.while_loop, warn=True) -> Callable[..., solver_protocols.Solution] ¤

Solve an initial value problem and return the solution at a pre-determined grid.

This algorithm implements the method by Krämer (2025). Please consider citing it if you use it for your research. A PDF is available here and Krämer's (2025) experiments are available here.

BibTex for Krämer (2025)
@InProceedings{kramer2024adaptive,
    title     = {Adaptive Probabilistic ODE Solvers Without Adaptive Memory
                Requirements},
    author    = {Kr{\"a}mer, Nicholas},
    booktitle = {Proceedings of the First International Conference on
                Probabilistic Numerics},
    pages     = {12--24},
    year      = {2025},
    editor    = {Kanagawa, Motonobu and Cockayne, Jon and Gessner, Alexandra
                and Hennig, Philipp},
    volume    = {271},
    series    = {Proceedings of Machine Learning Research},
    publisher = {PMLR},
    url       = {https://proceedings.mlr.press/v271/kramer25a.html}
}

solve_adaptive_terminal_values(solver: solver_protocols.Solver, error, control: controllers.Control | None = None, clip_dt: bool = True, while_loop: Callable = flow.while_loop) -> Callable[..., solver_protocols.Solution] ¤

Simulate the terminal values of an initial value problem.