Reference
Solvers¶
The numerical-method layer. Each solver is a SolverSpec carrying capability
flags (SolverCaps: explicit/implicit, adaptive, needs-Jacobian, supported
families); method= strings resolve against the registry, an unknown name
raises with the available list, and an auto-stiffness heuristic can pick an
implicit method from the Jacobian spectrum. Third-party solvers register
through the same entry point.
Most users never call these directly — they pass method="rk45" (or similar)
to a system's integrate (see
Integration & methods). This page
documents the registry itself.
Specs & capabilities¶
SolverSpec
dataclass
¶
SolverSpec(
name: str,
caps: SolverCaps,
kernel: str = "",
description: str = "",
origin: str = "builtin",
)
Registry metadata for one solver — the Python mirror of a Rust kernel.
| PARAMETER | DESCRIPTION |
|---|---|
name
|
The unique
TYPE:
|
caps
|
The solver's capabilities.
TYPE:
|
kernel
|
The name of the Rust kernel (
TYPE:
|
description
|
A one-line human description (shown in listings / docs).
TYPE:
|
origin
|
Where the spec came from — set to
TYPE:
|
SolverCaps
dataclass
¶
SolverCaps(
kind: str,
adaptive: bool = False,
needs_jacobian: bool = False,
supports: frozenset[str] = (
lambda: frozenset({"ode"})
)(),
)
A solver's capabilities — the Python mirror of tsdyn_solvers::Caps.
Drives method= resolution and the auto-stiffness layer (stream C-SOLV).
The four flags are how the selection layer reasons about a kernel without
running it: kind is the auto-stiffness axis (:func:~select.select
returns an "implicit" kernel only on a stiff RHS), needs_jacobian
drives the with_jacobian=True tape-build decision
(:func:~select.build_kwargs), supports gates the family check in
:func:~select.resolve, and adaptive is informational metadata exposed
through :attr:Resolution.adaptive (the engine, not this layer, acts on it).
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Whether the kernel is explicit or implicit. This is the primary auto-stiffness axis: the selection policy hands back an implicit kernel only when the RHS is judged stiff.
TYPE:
|
adaptive
|
Whether the kernel does its own embedded error estimate + step adaption
(an adaptive embedded pair / variable-order multistep), as opposed to a
fixed-step kernel where the caller controls
TYPE:
|
needs_jacobian
|
Whether the kernel requires the analytic Jacobian. Every implicit
kernel sets this; an explicit kernel may also (e.g. SDE Milstein reads
TYPE:
|
supports
|
The problem families the kernel can integrate; a subset of
|
supports_family
¶
Resolution & selection¶
resolve
¶
Resolve a method= string to a concrete, validated kernel.
Normalises the name (:func:normalize), applies the alias table, looks the
canonical name up in the registry, and — if family is given — checks the
kernel supports it. Built-in and plugin-registered solvers resolve
identically.
| PARAMETER | DESCRIPTION |
|---|---|
method
|
The requested method (e.g.
TYPE:
|
family
|
The problem family the kernel must support (
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Resolution
|
The canonical name, its spec, and the |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If method names no registered kernel (the message lists the available methods, with a stiff-family hint for SciPy/v2 stiff names), or if the kernel does not support family. |
Source code in src/tsdynamics/solvers/select.py
default_method
¶
Return the zero-config default kernel name for family.
| PARAMETER | DESCRIPTION |
|---|---|
family
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
str
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If family has no default kernel (e.g. |
Source code in src/tsdynamics/solvers/select.py
available_for
¶
Return the registered solver names, optionally filtered to family.
| PARAMETER | DESCRIPTION |
|---|---|
family
|
If given, keep only solvers whose caps support that problem family
(
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list[str]
|
Sorted solver names. |
Source code in src/tsdynamics/solvers/select.py
recommend
¶
recommend(
system: SystemBase,
*,
family: str = "ode",
ic: Any = None,
t: float = 0.0,
ratio_threshold: float = _DEFAULT_RATIO_THRESHOLD,
) -> Resolution
Recommend a solver for system, auto-selecting implicit on a stiff RHS.
Combines the detector (:func:is_stiff) with the policy (:func:select):
probes the Jacobian spectrum and resolves to the family's stiff kernel when
stiff, the explicit default otherwise. The returned :class:Resolution is
always resolvable for family, so this never raises on the selection itself.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
The system to integrate.
TYPE:
|
family
|
The problem family. Only
TYPE:
|
ic
|
Forwarded to :func:
TYPE:
|
t
|
Forwarded to :func:
TYPE:
|
ratio_threshold
|
Forwarded to :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Resolution
|
The recommended kernel, ready to feed to the engine (and its
:attr: |
Source code in src/tsdynamics/solvers/select.py
is_stiff
¶
is_stiff(
system: SystemBase,
*,
ic: Any = None,
t: float = 0.0,
ratio_threshold: float = _DEFAULT_RATIO_THRESHOLD,
) -> bool
Cheap a-priori stiffness test from the Jacobian spectrum at one point.
Evaluates the analytic Jacobian ∂f/∂u at (ic, t) (via the pure-Python
reference evaluator — no compiled engine needed), takes its eigenvalues, and
reports stiff when the stiffness ratio — the largest decay rate over the
smallest non-negligible decay rate — exceeds ratio_threshold while at least
one genuinely fast decaying mode is present.
This is a one-point heuristic meant to seed solver choice, not a guarantee:
the engine's runtime detector (rejected-step ratio over a window) is the
authoritative signal once integration is underway. Returns False
conservatively when the Jacobian cannot be formed or has no decaying mode.
.. note::
Only decay (real-part) stiffness is detected — a wide spread of
negative real parts. Oscillatory stiffness (eigenvalues with a large
imaginary part but a small real part, e.g. a fast-rotating but barely
damped mode that still bounds an explicit step by stability) is not
flagged: the ratio is built from real parts only, so such a spectrum
reports False here. The engine's runtime rejected-step detector
remains authoritative for those cases — pass an explicit stiff
method= (e.g. "bdf") if you know the RHS is oscillatorily stiff.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
An ODE system (anything :func:
TYPE:
|
ic
|
Point to evaluate the Jacobian at. Defaults to the system's resolved IC.
TYPE:
|
t
|
Time to evaluate at (for non-autonomous systems).
TYPE:
|
ratio_threshold
|
Stiffness-ratio boundary above which the RHS is called stiff.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
bool
|
|
Source code in src/tsdynamics/solvers/select.py
build_kwargs
¶
Return the build_problem kwargs implied by method.
{"with_jacobian": True} when the resolved kernel needs the Jacobian
(every implicit method, plus Milstein), else {}. The family
engine-dispatch seam merges this into its
:func:~tsdynamics.engine.problem.build_problem call so the stiff path
"just works" — an implicit method= produces a Jacobian-carrying tape and
the engine's Jacobian guard (PR #74) is satisfied rather than raising.
| PARAMETER | DESCRIPTION |
|---|---|
method
|
The requested
TYPE:
|
family
|
The problem family, forwarded to :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[str, bool]
|
|
Source code in src/tsdynamics/solvers/select.py
Registration¶
register
¶
register(
spec: SolverSpec, *, override: bool = False
) -> None
Register spec under its :attr:~SolverSpec.name.
| PARAMETER | DESCRIPTION |
|---|---|
spec
|
The solver metadata to register.
TYPE:
|
override
|
If False (the default), registering a name that already exists raises
:class:
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If spec's name is already registered and |