Compiled ODE/DDE integration and Lyapunov analysis for dynamical systems.
Project description
tsdynamics
Adaptive, compiled integration for ODEs (JiTCODE) and DDEs (JiTCDDE) with a small, clean API. You write the math; we handle compilation, tolerances, trajectories, and Lyapunov spectra.
- ODE base:
DynSys→ JiTCODE (jitcode) - DDE base:
DynSysDelay→ JiTCDDE (jitcdde) - Lyapunov spectra:
jitcode_lyap/jitcdde_lyap - Parameters are simple dicts exposed as attributes
- Deterministic output grids via
generate_timesteps
Contents
Install
You’ll need a C/C++ toolchain for JiTCODE/JiTCDDE.
- Linux:
sudo apt-get install build-essential python3-dev(Debian/Ubuntu) - macOS:
xcode-select --install - Windows: Install “Microsoft C++ Build Tools” (VS Build Tools)
Option A — uv (recommended)
# create & activate venv
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# library only
uv pip install .
# or editable + dev tools (tests, lint, etc.)
uv pip install -e ".[dev]"
# docs extras:
uv pip install -e ".[docs]"
Option B — pip (plain)
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install .
# or
pip install -e ".[dev]" ".[docs]"
Option C — conda (env + pip for the package)
conda create -n tsdynamics python=3.12 -y
conda activate tsdynamics
pip install .
# or
pip install -e ".[dev]" ".[docs]"
Dependencies (from
pyproject.toml):numpy>=2,<3,scipy>=1.14,<2,matplotlib>=3.10.6,numba==0.62.1,jitcdde==1.8.3,jitcode==1.7.3,symengine==0.14.1,sympy==1.14.0.
Quickstart
ODE — Lorenz
import numpy as np
from tsdynamics.base.ode_base import DynSys
class Lorenz(DynSys):
params = {"sigma": 10.0, "rho": 28.0, "beta": 8.0/3.0}
n_dim = 3
@staticmethod
def _rhs(y, t, beta, rho, sigma):
x, yv, z = y(0), y(1), y(2)
return (sigma*(yv - x), rho*x - x*z - yv, x*yv - beta*z)
lor = Lorenz()
t, X = lor.integrate(dt=0.01, final_time=50.0, method="dop853", rtol=1e-8, atol=1e-10)
exps = lor.lyapunov_spectrum(dt=0.1, burn_in=50.0, final_time=300.0,
method="dop853", rtol=1e-8, atol=1e-10)
print(exps) # ~[0.91, ~0, -14.57]
DDE — Mackey–Glass
import numpy as np
from tsdynamics.base.dde_base import DynSysDelay
class MackeyGlass(DynSysDelay):
params = {"beta": 0.2, "gamma": 0.1, "tau": 17.0, "n": 10}
n_dim = 1
@staticmethod
def _rhs(y, t, beta, gamma, tau, n):
y_tau = y(0, t - tau)
y_now = y(0, t)
return [beta * y_tau / (1 + y_tau**n) - gamma * y_now]
mg = MackeyGlass()
history = lambda s: [1.0 + 0.1*np.sin(0.2*s)] # avoid trivial equilibrium history
t, y = mg.integrate(dt=0.05, final_time=200.0, history=history, rtol=1e-8, atol=1e-10)
exps = mg.lyapunov_spectrum(n_lyap=1, dt=0.2, burn_in=100.0, final_time=600.0,
history=history, rtol=1e-8, atol=1e-10)
print(exps) # small positive (~1e-3)
ODE — Lorenz–96 (periodic)
class Lorenz96(DynSys):
params = {"f": 8.0, "N": 20}
n_dim = 20
@staticmethod
def _rhs(y, t, f, N):
return [ (y((i+1)%N) - y((i-2)%N)) * y((i-1)%N) - y(i) + f
for i in range(N) ]
l96 = Lorenz96()
t, X = l96.integrate(dt=0.05, final_time=50.0, method="dop853", rtol=1e-8, atol=1e-10)
ODE — Kuramoto–Sivashinsky (FD, periodic)
class KuramotoSivashinsky(DynSys):
def __init__(self, N, L, initial_conds=None):
if N < 5: raise ValueError("N >= 5 required")
super().__init__(n_dim=N, params={"N": int(N), "L": float(L)}, initial_conds=initial_conds)
@staticmethod
def _rhs(y, t, N, L):
dx = L / N
inv_dx, inv_dx2, inv_dx4 = 1.0/dx, 1.0/dx**2, 1.0/dx**4
rhs = []
for j in range(N):
jm2, jm1, jp1, jp2 = (j-2)%N, (j-1)%N, (j+1)%N, (j+2)%N
u = y(j)
nonlinear = - (y(jp1)**2 - y(jm1)**2) * (0.25*inv_dx) # -0.5*(u^2)_x
uxx = (y(jp1) - 2*u + y(jm1)) * inv_dx2
uxxxx = (y(jm2) - 4*y(jm1) + 6*u - 4*y(jp1) + y(jp2)) * inv_dx4
rhs.append(nonlinear - uxx - uxxxx)
return rhs
ks = KuramotoSivashinsky(N=64, L=32.0, initial_conds=1e-2*np.random.randn(64))
t, U = ks.integrate(dt=0.1, final_time=300.0, method="dop853", rtol=1e-8, atol=1e-10)
Define your own system
ODE (DynSys)
- Set
params(dict) andn_dim(int). - Implement static
_rhs(y, t, **params); returnn_dimexpressions. Usey(i)to access state components.
class MyODE(DynSys):
params = {"a": 2.0, "b": 0.5}
n_dim = 2
@staticmethod
def _rhs(y, t, a, b):
x, z = y(0), y(1)
return (a*x - b*z, x + z - x*z)
DDE (DynSysDelay)
- Same pattern but delayed access is
y(i, t - tau).
class MyDDE(DynSysDelay):
params = {"tau": 1.5, "k": 2.0}
n_dim = 1
@staticmethod
def _rhs(y, t, tau, k):
x_tau = y(0, t - tau)
x_now = y(0, t)
return [k * x_tau - x_now]
Map (DynMap)
- Same pattern but
iterateis used instead ofintegrate.
class MyMap(DynMap):
params = {"a": 2.0, "b": 0.5}
n_dim = 2
@staticmethod
def _rhs(y, a, b):
x, z = y(0), y(1)
return (a*x - b*z, x + z - x*z)
Lyapunov spectra
# ODE
exps = obj.lyapunov_spectrum(
dt=0.1, burn_in=50.0, final_time=300.0,
n_lyap=None, method="dop853", rtol=1e-8, atol=1e-10
)
# DDE
exps = obj.lyapunov_spectrum(
n_lyap=2, dt=0.2, burn_in=100.0, final_time=600.0,
history=history, rtol=1e-8, atol=1e-10
)
- ODE: time-weight the local LEs; constant
dt≈ simple mean. - DDE: use the weight returned by
jitcdde_lyapat each step.
Contributing
We welcome sharp, clean contributions. See CONTRIBUTING.md for setup (uv/pip/conda), style, tests, and PR workflow.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tsdynamics-0.1.0.tar.gz.
File metadata
- Download URL: tsdynamics-0.1.0.tar.gz
- Upload date:
- Size: 65.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dd1803cd7e75a9754454a3de7890268d1a286729fb5ce0b394a3286bcf1a0adb
|
|
| MD5 |
728ae27ee0d9f8492472176709f80797
|
|
| BLAKE2b-256 |
e5e2ba9e8f25fdd558cc4be8b855692746bb4715f621816f38fd2da0eeb7fba8
|
Provenance
The following attestation bundles were made for tsdynamics-0.1.0.tar.gz:
Publisher:
publish.yml on El3ssar/TSDynamics
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tsdynamics-0.1.0.tar.gz -
Subject digest:
dd1803cd7e75a9754454a3de7890268d1a286729fb5ce0b394a3286bcf1a0adb - Sigstore transparency entry: 1262904486
- Sigstore integration time:
-
Permalink:
El3ssar/TSDynamics@e8ef7240fa59d1ba61eb105ca2409562621e9632 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/El3ssar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e8ef7240fa59d1ba61eb105ca2409562621e9632 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tsdynamics-0.1.0-py3-none-any.whl.
File metadata
- Download URL: tsdynamics-0.1.0-py3-none-any.whl
- Upload date:
- Size: 65.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f492d0f100e39d9cc24f0d351d8131e6d7df9eb61fae047e1231b0cfedb10d99
|
|
| MD5 |
986c1fb906bfc993d9148ef2263d1cfa
|
|
| BLAKE2b-256 |
35e167958aca7f3e33cf5342ef58b0d8bde40b3672966e1e6265092d581e0a5a
|
Provenance
The following attestation bundles were made for tsdynamics-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on El3ssar/TSDynamics
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tsdynamics-0.1.0-py3-none-any.whl -
Subject digest:
f492d0f100e39d9cc24f0d351d8131e6d7df9eb61fae047e1231b0cfedb10d99 - Sigstore transparency entry: 1262904503
- Sigstore integration time:
-
Permalink:
El3ssar/TSDynamics@e8ef7240fa59d1ba61eb105ca2409562621e9632 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/El3ssar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@e8ef7240fa59d1ba61eb105ca2409562621e9632 -
Trigger Event:
push
-
Statement type: