lyapax
JAX-native Lyapunov exponent computation for ODEs and DDEs, via the
Benettin/QR method with jax.jvp/jax.vmap tangent propagation.
📚 Documentation
Looking for the documentation?
➡️ https://lyapax.readthedocs.io/en/latest/
It includes:
- Installation guide
- Quick start tutorial
- API reference
- Examples and gallery
- Validation, benchmarks, and implementation details
Install
pip install lyapax
Development installation
If you plan to contribute, run the test suite, or build the documentation, clone the repository and install the appropriate extras:
pip install -e ".[dev]" # testing
pip install -e ".[examples]" # example scripts
pip install -e ".[docs]" # documentation
pip install -e ".[adaptive]" # Diffrax backend
Requires jax>=0.10, Python >=3.11.
Enable float64 before anything else. Lyapunov exponents are averages of
log-growth rates accumulated over many steps; JAX's default float32
silently degrades long-horizon estimates. Do this first, before creating
any jax.numpy arrays:
import jax
jax.config.update("jax_enable_x64", True)
lyapunov_spectrum/lyapunov_spectrum_dde warn if called with a float32
state0 and x64 is off.
Minimal example
import jax
jax.config.update("jax_enable_x64", True)
import jax.numpy as jnp
from lyapax import lyapunov_spectrum, ode_problem
sigma, rho, beta = 10.0, 28.0, 8.0 / 3.0
def rhs(state):
x, y, z = state
return jnp.array([sigma * (y - x), x * (rho - z) - y, x * y - beta * z])
problem = ode_problem(rhs, state0=jnp.array([1.0, 1.0, 1.0]), dt=1e-2)
result = lyapunov_spectrum(
problem, n_steps=50_000, renorm_every=10, t_transient=100.0,
)
print(result.exponents) # ~ [0.906, 0.0, -14.57]
result.history gives the running per-column estimate at each
renormalization point, for checking convergence.
rhs is just a plain state -> jnp.ndarray function - write your own for
any system. lyapax.systems has this Lorenz system and other standard
test systems (Rössler, Kuramoto, ...) prebuilt, if you'd rather not retype
them.
Method and scope
lyapunov_spectrum propagates a (d, k) matrix of tangent vectors
alongside the trajectory via jax.jvp (one forward-mode pass per tracked
column, batched with jax.vmap), QR-decomposes it every renorm_every
steps, accumulates log|diag(R)|, and divides by elapsed time
(Benettin's method). k defaults to the full spectrum (k = d); cost
scales with k, not d, so partial spectra of high-dimensional systems
are cheap.
lyapunov_spectrum_dde generalizes this to fixed-delay DDEs by
differentiating through an augmented (state, ring_buffer) carry.
What this does not do:
- No stiff (implicit) ODE solvers.
step_fnis whatever integrator you hand it - a fixed-step map (ode_problem(..., integrator="rk4")by default, directrk4_step/euler_stepstep functions, or Euler/Heun/RK6 vialyapax.simulator), or an adaptive-step explicit one via the optionaladaptiveextra (lyapax.adaptive.diffrax_adaptive_step, backed by diffrax;ode_problemonly - notnetwork_problemor DDEs). Exponents are for the numerical time-dtmap (or accepted-step sequence, for the adaptive integrator), not an exact flow - checkdt/tolerance convergence for anything you report. The adaptive integrator is for tolerance-driven accuracy control, not speed - measured 2-4x slower thanrk4/rk6at matched accuracy, not faster; seedocs/background/capabilities.md. - DDE delays must be known and fixed, resolved one of two ways. By
default (grid-snapped), a physical delay
tauis rounded to the nearest multiple ofdt(lyapax.dde.resolve_tau_steps); uselyapax.dde.tau_effto see the delay actually used, and shrinkdtto converge it towardtau. Passinginterpolate=Trueinstead reconstructs the delayed history with a cubic-Hermite interpolant, removing that rounding on the uniform-delay path at some extra cost - see13_dde_history_interpolation.pyanddocs/background/lyapax_implementation.md. historycolumns are ordered once, by the final row. Near- degenerate exponents can swap order over the run - seeLyapunovResult.history's docstring.
Examples
Runnable, sphinx-gallery-formatted demos in examples/ (pip install -e ".[examples]", then python examples/01_linear_ode.py):
| File | Task |
|---|---|
00_time_series_sanity_check.py |
Inspecting the raw simulated time series before computing exponents |
01_linear_ode.py |
Exact-eigenvalue sanity check for the QR engine |
01b_custom_system.py |
Writing your own rhs from scratch, no lyapax.systems |
02_chaotic_maps.py |
1D/2D maps with closed-form exponents |
03_chaotic_flows.py |
Lorenz/Rössler, the standard chaotic-ODE benchmarks |
04_linear_network.py |
Coupled network vs. exact Jacobian eigenvalues |
05_kuramoto_sync.py |
Kuramoto network synchronization transition |
06_custom_coupling.py |
Writing a custom coupling function |
07_speed_and_accuracy.py |
Cost/accuracy tradeoffs across settings |
08_delayed_coupling.py |
Two-node linear DDE network vs. delay |
09_kuramoto_delayed_network.py |
Effect of transmission delay on a Kuramoto network |
10_matrix_free_scaling.py |
Dense jacfwd vs. jvp/vmap, and why it matters for partial spectra |
11_vmap_parameter_sweep.py |
Batched parameter sweeps via jax.vmap |
12_public_api_overview.py |
Problem-object API for ODEs, networks, and DDEs |
13_dde_history_interpolation.py |
Grid-snapped vs. Hermite-interpolated DDE history reads |
14_gpu_acceleration.py |
When GPU execution pays off for larger Lyapunov workloads |
15_adaptive_ode.py |
Adaptive-step ODE integration via the optional diffrax backend |
16_convergence_drift.py |
Chunked run-inspect-resume loop with convergence_drift |
17_dde_resume.py |
Resuming a checkpointed DDE run |
18_differentiate_lyapunov_exponent.py |
Differentiating an exponent w.r.t. a system parameter, and where that breaks down |
19_kaplan_yorke_dimension.py |
Kaplan-Yorke dimension from a Lyapunov spectrum |
Building the docs
pip install -e ".[docs,adaptive]"
sphinx-build -b html docs docs/_build/html
This re-runs every numbered examples/*.py file to render it into a
sphinx-gallery gallery (code, output, and plots), including
15_adaptive_ode.py (hence the adaptive extra above), alongside the
API reference; open docs/_build/html/index.html when it's done.
Further reading
docs/background/validation.md- the correctness tests this package is held to (exact values, structural invariants, literature figures).docs/background/benchmarks.md- comparison againstjitcode/jitcddeandChaosTools.jl.docs/background/capabilities.md- a candid summary of what lyapax does and does not do, including measured performance characteristics.
Metadata
Release files for lyapax 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| lyapax-0.3.0.tar.gz | 84.8 kB | With provenance | Details for lyapax-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| lyapax-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for lyapax-0.3.0-py3-none-any.whl |
Total release size: 146.1 kB
Release files / lyapax-0.3.0.tar.gz
| Download URL | lyapax-0.3.0.tar.gz |
|---|---|
| Size | 84.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f62f2f825464b49647b9ae895adb68570d6c9132b9d20fbe64fe6e95dd5b9f74
|
|
BLAKE2b-256 checksum How to use checksums |
07c8c3d88f1a09472dd812c22d8164443afa7a323482cf3da9a1c7fe080244e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logRelease files / lyapax-0.3.0-py3-none-any.whl
| Download URL | lyapax-0.3.0-py3-none-any.whl |
|---|---|
| Size | 61.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6093dede63ccef85a337b23fe1ec9fb51dfb75b696bf8454a0d5dfc0c9c3c82e
|
|
BLAKE2b-256 checksum How to use checksums |
df376660f6c034e9ef2a687d72df05e3f61305004bd6c44729bf0b68061fb92a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logSecurity for lyapax 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for lyapax-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.