Reference
Base classes¶
The shared machinery (ParamSet, MetaStore, Trajectory,
SystemBase), the three family bases users subclass, and the System
protocol the analysis toolkit is written against.
ParamSet
¶
Bases: MutableMapping
Ordered, fixed-key parameter container.
Keys are frozen at construction time — you can change values but not add or
remove keys. Supports both dict-style (p["sigma"]) and attribute-style
(p.sigma) read/write.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Initial key→value mapping. All future writes must use existing keys.
TYPE:
|
Examples:
>>> p = ParamSet({"sigma": 10.0, "rho": 28.0})
>>> p.sigma
10.0
>>> p.sigma = 15.0
>>> p["sigma"]
15.0
>>> p.unknown = 5.0 # raises AttributeError
Source code in src/tsdynamics/families/base.py
param_hash
¶
param_hash() -> int
Return a process-stable 64-bit integer hash of the current parameter values.
Uses MD5 over a JSON-serialised representation so the result is
reproducible across Python process restarts (unlike hash()).
The hash backs cache keys for compiled JiTCODE / JiTCDDE modules
and the Numba-iterate cache. At 64 bits the birthday-paradox
collision probability reaches 50 % only around 2^32 ≈ 4·10⁹
distinct parameter sets, which is well beyond any realistic
parameter sweep. The previous 32-bit width hit the same threshold
at only 2^16 ≈ 65 000 sets, which a sufficiently large sweep
could plausibly reach — and a collision there would silently
return a compiled artifact built for a different parameter set.
Source code in src/tsdynamics/families/base.py
MetaStore
¶
Bases: MutableMapping
Append-with-history metadata store for computed results.
Behaves like a dict for everyday use (meta["lyapunov_spectrum"]
reads/writes the latest value), but every write is appended rather
than overwritten, so earlier results survive::
sys.meta.record("lyapunov_spectrum", spec, dt=0.1, final_time=200.0)
sys.meta["lyapunov_spectrum"] # latest value
sys.meta.history("lyapunov_spectrum") # every record, with context
Equality compares the latest values against a plain dict (or another
MetaStore), preserving sys.meta == {} style assertions.
Source code in src/tsdynamics/families/base.py
Trajectory
¶
The result of integrating or iterating a dynamical system.
Supports tuple-unpacking for backward compatibility::
t, y = system.integrate(final_time=100)
| ATTRIBUTE | DESCRIPTION |
|---|---|
t |
Time points (or step indices for discrete maps).
TYPE:
|
y |
State at each time point.
TYPE:
|
system |
Back-reference to the system that produced this trajectory.
TYPE:
|
meta |
Provenance: system name, params snapshot, solver, tolerances, ic.
TYPE:
|
Examples:
>>> traj = lor.integrate(final_time=100)
>>> traj.dim
3
>>> traj["x"] # named component (via the class's ``variables``)
array([...])
>>> traj.after(20.0) # drop transient
Trajectory(n_steps=..., dim=3, t=[20.0, 100.0])
>>> t, y = traj # tuple-unpack still works
Source code in src/tsdynamics/data/trajectory.py
variables
property
¶
Component names declared by the system (instance attr or class ClassVar).
component
¶
Return a single state component.
| PARAMETER | DESCRIPTION |
|---|---|
i
|
Component index, or component name when the system declares
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(T))
|
|
Source code in src/tsdynamics/data/trajectory.py
after
¶
after(t0: float) -> Trajectory
Drop the initial transient.
| PARAMETER | DESCRIPTION |
|---|---|
t0
|
Keep only time points
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
|
Source code in src/tsdynamics/data/trajectory.py
minmax
¶
standardize
¶
standardize() -> Trajectory
Return a copy with zero mean and unit standard deviation per component.
The applied transform is recorded in meta["standardized"].
Source code in src/tsdynamics/data/trajectory.py
neighbors
¶
Nearest trajectory points to query point(s) q.
Builds a KD-tree lazily on first call and caches it; subsequent queries are O(log T).
| PARAMETER | DESCRIPTION |
|---|---|
q
|
Query point(s). |
k
|
Number of neighbours per query point.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(distances, indices)
|
As returned by :meth: |
Source code in src/tsdynamics/data/trajectory.py
set_distance
¶
Distance to another point set (Trajectory or array), as a set.
method is "centroid" (default), "hausdorff", or
"minimum" — see :func:tsdynamics.data.set_distance. The
matching primitive behind attractor deduplication and continuation.
Source code in src/tsdynamics/data/trajectory.py
SystemBase
¶
Abstract base class for all dynamical systems.
Provides:
- params — a :class:ParamSet holding the system's parameter values.
Attribute access on the system is transparently forwarded to params.
- dim — integer state-space dimension.
- ic — optional initial conditions array.
- meta — dict for storing computed metadata (Lyapunov spectra, etc.).
- copy() / with_params() for safe cloning.
- resolve_ic() for uniform IC resolution across subclasses.
Class-level declarations
Subclasses should declare at class level::
class Lorenz(ContinuousSystem):
params = {"sigma": 10.0, "rho": 28.0, "beta": 8/3}
dim = 3
Constructor overrides
Individual instances can override params and/or ic::
lor = Lorenz(params={"rho": 30.0}, ic=[1.0, 0.0, 0.0])
Constructor will raise ValueError for unknown param keys.
Source code in src/tsdynamics/families/base.py
copy
¶
copy() -> SystemBase
Return a deep copy with the same class, params, and ic.
The copy has its own independent params and meta stores.
Source code in src/tsdynamics/families/base.py
with_params
¶
with_params(**overrides) -> SystemBase
Return a new system with some parameters overridden.
Does not mutate self. Designed for parameter sweeps::
for rho in np.linspace(0, 50, 200):
traj = base_system.with_params(rho=rho).integrate(final_time=50)
| PARAMETER | DESCRIPTION |
|---|---|
**overrides
|
New parameter values. Keys must exist in
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
SystemBase
|
New instance of the same subclass. |
Source code in src/tsdynamics/families/base.py
resolve_ic
¶
Resolve initial conditions consistently.
Priority:
icargument (if provided)self.ic(set by a previous integration / iteration)type(self).default_ic(class-level default, if declared)- Random
U[0, 1)^dim
The resolved IC is stored in self.ic so subsequent calls without
an explicit ic reproduce the same initial state.
| PARAMETER | DESCRIPTION |
|---|---|
ic
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim))
|
|
Source code in src/tsdynamics/families/base.py
ContinuousSystem
¶
Bases: SystemBase, ABC
Base class for ODE-based dynamical systems, compiled via JiTCODE.
Subclass contract
- Declare
params = {...}anddim = Nat class level. - Implement
_equationsas a@staticmethodreturning a length-dimsequence of JiTCODE / SymEngine symbolic expressions. - Optionally mark integer or loop-structural parameters in
_structural_params— these are baked into the compiled C code rather than exposed as runtime control parameters.
Compilation
The first call to integrate or lyapunov_spectrum triggers JiTCODE
compilation. Non-structural parameters become JiTCODE control_pars,
meaning the resulting .so module is compiled once per class (or
once per structural-param combination) and reused for all subsequent runs
and parameter changes, even across process restarts.
Class-level attributes
_structural_params : frozenset[str]
Parameter names that appear as integer loop bounds or affect the
symbolic structure of _equations. These are baked in at compile
time. For most systems this is empty (the default).
Example — Lorenz96 uses ``N`` to build the list comprehension::
_structural_params = frozenset({"N"})
_default_method : str
Default integrator name (default "RK45").
Examples:
>>> lor = Lorenz()
>>> traj = lor.integrate(final_time=100, dt=0.01)
>>> t, y = traj # tuple-unpack
>>> lor.sigma = 15.0 # change param — zero recompile cost
>>> traj2 = lor.integrate(final_time=100)
Source code in src/tsdynamics/families/base.py
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict | None = None,
method: str | None = None,
rtol: float = 1e-06,
atol: float = 1e-09,
**integrator_kwargs,
) -> None
(Re)start the incremental stepper from state u at time t.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
Initial state (falls back to
TYPE:
|
t
|
Start time (default 0.0).
TYPE:
|
params
|
Parameter overrides applied (in place) before restarting.
TYPE:
|
method
|
Stepper configuration, as in :meth:
TYPE:
|
rtol
|
Stepper configuration, as in :meth:
TYPE:
|
atol
|
Stepper configuration, as in :meth:
TYPE:
|
**integrator_kwargs
|
Stepper configuration, as in :meth:
TYPE:
|
Source code in src/tsdynamics/families/continuous.py
step
¶
Advance the system by dt (default 0.01) and return the new state.
The first call performs an implicit :meth:reinit. Parameter changes
made after reinit take effect on the next reinit, not on a
live stepper.
Source code in src/tsdynamics/families/continuous.py
set_state
¶
set_state(u: Any) -> None
Overwrite the current state without changing the current time.
Source code in src/tsdynamics/families/continuous.py
trajectory
¶
trajectory(
final_time: float = 100.0,
*,
dt: float = 0.02,
transient: float = 0.0,
**kwargs,
) -> Trajectory
Protocol-uniform trajectory: integrate plus optional transient drop.
Source code in src/tsdynamics/families/continuous.py
jacobian_sym
¶
Return the symbolic Jacobian of _equations, differentiated by SymEngine.
Rows are d f_i / d y(j) for the current structural parameters;
non-structural parameters appear as symbols. Hand-written
_jacobian methods on system classes are never used at runtime —
this autogenerated form is the single source of truth (the test suite
cross-checks hand-written ones against it).
| RETURNS | DESCRIPTION |
|---|---|
list of ``dim`` rows, each a list of ``dim`` SymEngine expressions.
|
|
Source code in src/tsdynamics/families/continuous.py
jacobian
¶
Evaluate the (autogenerated) Jacobian numerically at state u.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
State at which to evaluate.
TYPE:
|
t
|
Time (matters only for non-autonomous systems).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim, dim))
|
|
Source code in src/tsdynamics/families/continuous.py
integrate
¶
integrate(
final_time: float = 100.0,
dt: float = 0.02,
*,
t0: float = 0.0,
ic: Any | None = None,
method: str | None = None,
rtol: float = 1e-06,
atol: float = 1e-09,
backend: str | None = None,
**integrator_kwargs,
) -> Trajectory
Integrate the ODE and return a :class:~tsdynamics.families.Trajectory.
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
End of integration window. Default 100.0.
TYPE:
|
dt
|
Output sampling interval. The internal stepper is adaptive.
TYPE:
|
t0
|
Start time. Default 0.0. Allows warm restarts from a non-zero
time (the IC is interpreted as the state at
TYPE:
|
ic
|
Initial state at
TYPE:
|
method
|
Integrator:
TYPE:
|
rtol
|
Solver tolerances (default 1e-6 / 1e-9).
TYPE:
|
atol
|
Solver tolerances (default 1e-6 / 1e-9).
TYPE:
|
backend
|
Where the ODE is integrated. Defaults to
TYPE:
|
**integrator_kwargs
|
Forwarded to
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
Supports tuple-unpacking: |
Source code in src/tsdynamics/families/continuous.py
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | |
lyapunov_spectrum
¶
lyapunov_spectrum(
final_time: float = 200.0,
dt: float = 0.1,
*,
ic: Any | None = None,
n_exp: int | None = None,
burn_in: float = 50.0,
method: str | None = None,
rtol: float = 1e-06,
atol: float = 1e-09,
**integrator_kwargs,
) -> ndarray
Estimate the Lyapunov spectrum using :func:jitcode.jitcode_lyap.
Results are stored in self.meta['lyapunov_spectrum'].
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
Averaging window length after burn-in. Default 200.0.
TYPE:
|
dt
|
Sampling interval for local exponent accumulation. Default 0.1.
TYPE:
|
ic
|
Initial state. Falls back to
TYPE:
|
n_exp
|
Number of exponents. Defaults to
TYPE:
|
burn_in
|
Discard this much time before averaging. Default 50.0.
TYPE:
|
method
|
Integrator (default
TYPE:
|
rtol
|
Tolerances.
TYPE:
|
atol
|
Tolerances.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n_exp))
|
Lyapunov exponents ordered from largest to smallest. |
Source code in src/tsdynamics/families/continuous.py
876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | |
DelaySystem
¶
Bases: SystemBase, ABC
Base class for delay differential systems (DDEs), compiled via JiTCDDE.
Subclass contract
- Declare
params = {...}anddim = N. - Implement
_equationsas a@staticmethodreturning a length-dimsequence of JiTCDDE symbolic expressions. Usey(i, t - tau)for delayed state access.
Compilation & caching
DDE systems cache compiled modules per (class, params_hash). Unlike
ODEs, delay values directly affect the history-buffer structure and cannot
easily be treated as runtime control parameters. The compiled .so is
saved to disk so subsequent runs with the same parameters reload instantly.
DDEs typically need looser tolerances than ODEs (start with rtol=atol=1e-3).
History
Pass a history callable h(s) → sequence defining the past for
s ≤ 0. If omitted, a constant past equal to ic is used.
.. note::
Provide a non-equilibrium history to avoid trivial Lyapunov exponents.
For lyapunov_spectrum, past_from_function is incompatible with
jitcdde_lyap; the workaround is to run integrate first with the
desired history, then pass the end-state as ic to
lyapunov_spectrum which uses constant_past internally.
Examples:
>>> mg = MackeyGlass()
>>> hist = lambda s: [1.0 + 0.1 * np.sin(0.2 * s)]
>>> traj = mg.integrate(final_time=500, history=hist)
>>> exps = mg.lyapunov_spectrum(n_exp=2, ic=traj.y[-1])
Source code in src/tsdynamics/families/base.py
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict | None = None,
rtol: float | None = None,
atol: float | None = None,
**kwargs,
) -> None
(Re)start the incremental stepper from a constant past equal to u.
DDE state is a history function; the protocol restart uses a
constant past (the same convention as lyapunov_spectrum). For a
custom history, use :meth:integrate with history= and continue
from traj.y[-1].
Source code in src/tsdynamics/families/delay.py
step
¶
Advance by dt (default 0.1, forward-only) and return the new state.
Source code in src/tsdynamics/families/delay.py
set_state
¶
set_state(u: Any) -> None
Not available for DDEs — their state is a whole history function.
Source code in src/tsdynamics/families/delay.py
trajectory
¶
trajectory(
final_time: float = 100.0,
*,
dt: float = 0.02,
transient: float = 0.0,
**kwargs,
) -> Trajectory
Protocol-uniform trajectory: integrate plus optional transient drop.
Source code in src/tsdynamics/families/delay.py
integrate
¶
integrate(
final_time: float = 100.0,
dt: float = 0.02,
*,
ic: Any | None = None,
history: History = None,
rtol: float | None = None,
atol: float | None = None,
backend: str | None = None,
method: str = "rk45",
**kwargs,
) -> Trajectory
Integrate the DDE and return a :class:~tsdynamics.families.Trajectory.
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
Integration end time. Default 100.0.
TYPE:
|
dt
|
Output sampling interval.
TYPE:
|
ic
|
Used for constant past when
TYPE:
|
history
|
TYPE:
|
rtol
|
Integration tolerances. DDEs typically need 1e-3; very tight tolerances can stall the solver.
TYPE:
|
atol
|
Integration tolerances. DDEs typically need 1e-3; very tight tolerances can stall the solver.
TYPE:
|
backend
|
Which engine integrates the DDE. Defaults to
TYPE:
|
method
|
The explicit kernel for the engine backends (
TYPE:
|
**kwargs
|
Forwarded to
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
|
Source code in src/tsdynamics/families/delay.py
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |
lyapunov_spectrum
¶
lyapunov_spectrum(
final_time: float = 200.0,
dt: float = 0.1,
*,
ic: Any | None = None,
n_exp: int = 1,
burn_in: float = 50.0,
rtol: float | None = None,
atol: float | None = None,
**kwargs,
) -> ndarray
Estimate the n_exp leading Lyapunov exponents via :class:jitcdde.jitcdde_lyap.
Results are stored in self.meta['lyapunov_spectrum'].
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
Averaging window after burn-in. Default 200.0.
TYPE:
|
dt
|
Sampling interval. Default 0.1.
TYPE:
|
ic
|
Initial state. Provide the end-state of a prior
TYPE:
|
n_exp
|
Number of leading exponents to estimate. DDEs have infinitely many; choose consciously. Default 1.
TYPE:
|
burn_in
|
Discard interval. Default 50.0.
TYPE:
|
rtol
|
Integration tolerances. Defaults to
TYPE:
|
atol
|
Integration tolerances. Defaults to
TYPE:
|
Notes
past_from_function is NOT used here because it is incompatible
with jitcdde_lyap internally. A constant past from ic is
used instead. For best results, pass ic=traj.y[-1] from a
prior integrate run — this places the trajectory on the
attractor and avoids trivial exponents from equilibrium pasts.
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n_exp))
|
|
Source code in src/tsdynamics/families/delay.py
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 | |
DiscreteMap
¶
Bases: SystemBase
Base class for discrete maps with Numba-accelerated iteration.
Subclass contract
- Declare
params = {...}anddim = N. - Implement
_stepand_jacobianas@staticjitstatic methods. Parameters arrive as positional arguments in the order they appear in the class-levelparamsdict.
Compilation
On the first call to iterate, a Numba-compiled loop is built that
inlines _step with the current parameter values baked in. This
eliminates per-step Python overhead. The compiled loop is cached in
_iter_cache per (class, params_hash); changing a parameter
triggers a fresh compile on the next call.
Lyapunov spectrum
Computed in a single forward pass via QR decomposition of the Jacobian product — no redundant second iteration over the trajectory.
Examples:
>>> h = Henon()
>>> traj = h.iterate(steps=10_000)
>>> t_idx, X = traj # tuple-unpack
>>> exps = h.lyapunov_spectrum(steps=5_000)
>>> h_variant = h.with_params(a=1.2)
>>> traj2 = h_variant.iterate(steps=10_000)
Source code in src/tsdynamics/families/base.py
reinit
¶
(Re)start stepping from state u at iteration count t.
Source code in src/tsdynamics/families/discrete.py
step
¶
Advance n iterations (default 1) and return the new state.
Source code in src/tsdynamics/families/discrete.py
trajectory
¶
trajectory(
steps: int = 1000, *, transient: int = 0, **kwargs
) -> Trajectory
Protocol-uniform trajectory: iterate plus optional transient drop.
Source code in src/tsdynamics/families/discrete.py
iterate
¶
iterate(
steps: int = 1000,
ic: Any | None = None,
max_retries: int = 10,
*,
backend: str | None = None,
) -> Trajectory
Iterate the map for steps steps.
Uses a Numba-compiled loop when available (significant speedup for
large steps or high-dim maps). Falls back to a Python loop
when Numba is not installed.
| PARAMETER | DESCRIPTION |
|---|---|
steps
|
Number of iterations. Default 1000.
TYPE:
|
ic
|
Initial state. Falls back to
TYPE:
|
max_retries
|
Retry with a new random IC if divergence is detected (Numba path only — the engine path raises on divergence instead of retrying).
TYPE:
|
backend
|
Where the iteration runs. Defaults to
Any non-
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
|
Source code in src/tsdynamics/families/discrete.py
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | |
lyapunov_spectrum
¶
lyapunov_spectrum(
steps: int = 5000,
ic: Any | None = None,
n_exp: int | None = None,
reortho_interval: int = 1,
) -> ndarray
QR-based Lyapunov spectrum.
Computes the spectrum in a single forward pass — the Jacobian is evaluated at each step alongside the trajectory, avoiding the redundant double iteration of the previous implementation.
Results are stored in self.meta['lyapunov_spectrum'].
| PARAMETER | DESCRIPTION |
|---|---|
steps
|
Number of iterations. Default 5000.
TYPE:
|
ic
|
Initial state. Falls back to
TYPE:
|
n_exp
|
Number of exponents. Defaults to
TYPE:
|
reortho_interval
|
Reorthonormalise every this many steps. Default 1.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n_exp))
|
|
Source code in src/tsdynamics/families/discrete.py
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 | |