Reference
Base classes¶
The shared machinery (ParamSet, Trajectory, SystemBase), the four family
bases users subclass — ContinuousSystem (ODEs), DelaySystem (DDEs),
DiscreteMap (maps) and StochasticSystem (diagonal-Itô SDEs) — and the
System protocol the analysis toolkit is written against.
run is the one verb that produces data on every one of them; reinit /
step / state / time are the stepping protocol underneath it.
ParamSet
¶
Ordered, fixed-key parameter container (a dict you cannot re-key).
Keys are frozen at construction — 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:
|
| RAISES | DESCRIPTION |
|---|---|
AttributeError
|
On attribute-style read or write of an undeclared key
( |
KeyError
|
On item-style write of an undeclared key ( |
InvalidInputError
|
On any structural mutation ( |
Examples:
>>> p = ParamSet({"sigma": 10.0, "rho": 28.0})
>>> p
{'sigma': 10.0, 'rho': 28.0}
>>> isinstance(p, dict)
True
>>> p.as_tuple()
(10.0, 28.0)
>>> p["sigma"] = 12.0
>>> p.sigma
12.0
>>> p["sigmaa"] = 1.0
Traceback (most recent call last):
...
KeyError: "Unknown parameter 'sigmaa'. Declared params: ['sigma', 'rho']"
Source code in src/tsdynamics/families/_params.py
pop
¶
Refuse — a parameter set has fixed keys.
popitem
¶
Refuse — a parameter set has fixed keys.
clear
¶
fromkeys
classmethod
¶
Refuse — a parameter set's keys come from the system that declares them.
The inherited dict.fromkeys was already uncallable in every
spelling: it builds the new mapping by calling cls(), and
ParamSet.__init__ requires data, so the class method died on a
raw TypeError: ParamSet.__init__() missing 1 required positional
argument: 'data' — an error about a constructor the caller never
wrote. Its three fixed-key siblings (pop / popitem /
clear) all answer by name, so this one does too.
Source code in src/tsdynamics/families/_params.py
setdefault
¶
Return self[key]; refuse to insert an undeclared key.
update
¶
Update values in place; refuse any key that is not declared.
Source code in src/tsdynamics/families/_params.py
as_tuple
¶
Return parameter values as a tuple (declaration order).
This ordering is the tape contract: the engine's lowering hands the
values to the kernel positionally, so re-ordering them silently swaps
parameters. dict preserves insertion order, which is why a plain
dict subclass is a safe home for it.
Source code in src/tsdynamics/families/_params.py
as_dict
¶
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 per-system lowering / lambdify caches.
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.
Source code in src/tsdynamics/families/_params.py
Trajectory
¶
Trajectory(
t: ndarray,
y: ndarray,
system: Any = None,
meta: dict[str, Any] | None = None,
*,
variables: Sequence[str] | None = None,
)
The result of integrating or iterating a dynamical system.
A trajectory is a container of samples: len(traj) is the number of
time samples and iterating it yields that many (t_i, y_i) pairs.
.. versionchanged:: 6.0
__iter__ used to be a tuple-unpacking convenience yielding the two
columns (t, y), which put it in direct contradiction with
__len__ (len(traj) said n_steps, len(list(traj)) said 2)
and broke the container contract for any code that sizes an iterable and
then walks it. Iteration now yields samples; the column unpacking moved
to the explicit :meth:unpack::
t, y = traj.unpack() # was: t, y = traj
| ATTRIBUTE | DESCRIPTION |
|---|---|
t |
Time points (or step indices for discrete maps).
TYPE:
|
y |
State at each time point. |
system |
Back-reference to the system that produced this trajectory.
TYPE:
|
meta |
Provenance: system name, params snapshot, solver, tolerances, ic.
TYPE:
|
Examples:
>>> traj = lor.run(final_time=100)
>>> traj.dim
3
>>> traj["x"] # named component (via the class's ``variables``)
array([...])
>>> traj.after(20.0) # drop transient
Trajectory of Lorenz (n: ..., dim: 3, var: x, y, z) t ∈ [20, 100]
measure it: ts.analysis.find(traj) · draw it: ts.plot(traj)
>>> t, y = traj.unpack() # the two columns
>>> for t_i, y_i in traj: # ... or walk the samples
... pass
Build a trajectory from time and state arrays.
| PARAMETER | DESCRIPTION |
|---|---|
t
|
Time points (or step indices).
TYPE:
|
y
|
State at each time point. A 1-D array is read as a single
component and stored as |
system
|
The system that produced this trajectory. Measured data has no
system, so it defaults to
TYPE:
|
meta
|
Provenance (system name, params, solver,
TYPE:
|
variables
|
One name per state component — what :meth: .. versionadded:: 6.0
It used to be reachable only as the undocumented
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
InvalidInputError
|
If |
Source code in src/tsdynamics/data/trajectory.py
unbounded
property
¶
What this orbit escaped to, or None if it stayed bounded.
A run that blows up without reaching the engine's hard 1e150 guard
comes back as an ordinary, finite, entirely meaningless trajectory —
measured, Chua().run(ic=[500, 0, 0]) returns max|y| = 1.6e9, no
exception, isnan all False. Every downstream answer taken from
it then looks exactly like an honest one. This is the flag that tells
the two apart: it is the line the repr prints, and what
:class:~tsdynamics.derived.poincare.PoincareSection carries through.
Computed on first read and cached, so a trajectory nobody asks about pays nothing.
| RETURNS | DESCRIPTION |
|---|---|
Unbounded or None
|
|
Examples:
dt
property
¶
dt: float | None
The sampling interval, derived from the t axis.
The axis is the one truth, and None when there is no single
answer — a non-uniform axis (a Poincaré section) or an axis too short to
have a step. meta["dt"] used to record what the run asked for and
slicing carried it verbatim, so a decimated trajectory reported the
undecimated step and every per-unit-time estimator taken from meta
was off by the decimation factor with no exception. Since v6 round 8
selecting rows re-derives meta["dt"] from the new axis
(:data:_ROW_DERIVED_META), so the two agree — but this property is
still the reading the library itself uses, because it cannot be absent.
Examples:
variables
property
¶
The name of every component — never None.
Resolution order: the names this trajectory carries in
meta["variables"] (written by a column selection, so a sub-trajectory
names its own columns), then the producing system's variables, then
the generated y0 … y{dim-1}.
.. versionchanged:: 6.0
This used to be a pass-through to system.variables returning
None for measured data — so a bare-array trajectory printed
y0/y1 column headings in :meth:to_frame and on its plot,
then refused traj["y0"]; and a column selection reported the
inner system's full tuple, which made traj["x", "z"]["y"]
silently return z. The names now come from one place and are
always as long as the state block is wide.
unpack
¶
Return the two column arrays (t, y).
The explicit spelling of what t, y = traj used to do implicitly (see
:meth:__iter__)::
t, y = system.run(final_time=100).unpack()
| RETURNS | DESCRIPTION |
|---|---|
tuple of (ndarray, ndarray)
|
|
Source code in src/tsdynamics/data/trajectory.py
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 — keep the samples at or after t0.
| PARAMETER | DESCRIPTION |
|---|---|
t0
|
A cut on the recorded time axis, read in that axis's own unit:
time units for a flow, the iteration index for a map, the crossing
time for a section. (It is a cut, not a horizon:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
The tail, sharing this trajectory's system and provenance. |
Source code in src/tsdynamics/data/trajectory.py
to_frame
¶
to_frame() -> Any
Return a :class:pandas.DataFrame view of the trajectory.
One row per time sample: the time vector becomes the index (named
"t") and each state component a column, named from the system's
variables when it declares them and y0…y{dim-1} otherwise. The
provenance travels along on frame.attrs["meta"].
pandas is a soft dependency imported lazily here, so importing
tsdynamics never pulls it in; a missing install raises an
:class:ImportError pointing at pip install pandas (and at the
traj.t / traj.y arrays, which need nothing extra).
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
Shape |
| RAISES | DESCRIPTION |
|---|---|
ImportError
|
If :mod: |
Source code in src/tsdynamics/data/trajectory.py
minmax
¶
Return the per-component extent — (lo, hi), each of shape (dim,).
Unpacks like the bare tuple it used to be (lo, hi = traj.minmax())
and names its halves (traj.minmax().hi), so a reader of the call
site does not have to remember which one comes first.
Source code in src/tsdynamics/data/trajectory.py
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
¶
Return the trajectory states nearest 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). A single point gives a result without the leading
query axis; |
k
|
Number of neighbours per query point. The
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Neighbors
|
|
.. versionchanged:: 6.0
|
This handed back |
Examples:
>>> import numpy as np, tsdynamics as ts
>>> tr = ts.systems.Lorenz().run(final_time=1.0, dt=0.1, ic=[1.0, 1.0, 1.0])
>>> near = tr.neighbors([1.0, 1.0, 1.0], k=2)
>>> near.state.shape, near.distance.shape, near.index.shape
((2, 3), (2,), (2,))
Source code in src/tsdynamics/data/trajectory.py
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 | |
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
¶
SystemBase(
params: dict[str, Any] | None = None,
ic: Any | None = None,
dim: int | None = None,
field_shape: tuple[int, ...] | None = None,
seed: int | None = None,
**param_kwargs: Any,
)
Bases: DeriveMixin, SystemPlottable
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
Every declared parameter is a plain constructor keyword — the most natural line a user will type::
lor = Lorenz(rho=30.0, ic=[1.0, 0.0, 0.0])
The params= dict spelling stays supported and means exactly the same
thing (it is the way to pass a name computed at runtime)::
lor = Lorenz(params={"rho": 30.0}, ic=[1.0, 0.0, 0.0])
The constructor raises
:class:~tsdynamics.errors.InvalidParameterError (a ValueError
subclass) for any unknown parameter key — whether it arrives as
params={"rhoo": 30.0} or as rhoo=30.0 — so a typo fails loudly,
naming the declared parameters, instead of being silently ignored. Giving
the same parameter through both channels is also an error: there is
deliberately no precedence rule to memorise.
The five keywords :meth:__init__ reserves for itself (params, ic,
dim, field_shape, seed) cannot also be parameter names — a class
declaring one is refused at definition time by :meth:__init_subclass__
rather than silently shadowing it.
See Also
ParamSet : the fixed-key parameter container behind params.
MetaStore : the append-with-history store behind meta.
_resolve_ic : the uniform initial-condition resolution helper.
Initialise a system from its class defaults plus instance overrides.
| PARAMETER | DESCRIPTION |
|---|---|
params
|
Per-instance parameter overrides. Every key must already exist in
the class-level :attr:
TYPE:
|
ic
|
Initial conditions. Stored on
TYPE:
|
dim
|
State-space dimension override for variable-dimension systems. Falls
back to the class-level :attr:
TYPE:
|
field_shape
|
Spatial grid shape override for a spatially-extended system (see
:attr:
TYPE:
|
seed
|
Seed for the initial-condition draw used when neither
TYPE:
|
**param_kwargs
|
Per-parameter overrides given as plain keywords —
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If |
Examples:
>>> Lorenz(sigma=12.0).sigma # the natural spelling
12.0
>>> Lorenz(params={"sigma": 12.0}).sigma # equivalent, still supported
12.0
Source code in src/tsdynamics/families/base.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | |
family
property
¶
family: str
"ode" | "dde" | "map" | "sde" — what kind of dynamics this is.
Replaces is_discrete, which could not tell a delay system from a
stochastic one, so every caller that needed the distinction had to sniff
the class.
info
property
¶
Everything true about this system, in one printable record.
Absorbs reference, doi, known_lyapunov, field_labels and
default_ic off the tab surface — they are facts, and a fact belongs
in the record you print::
>>> print(Rossler().info) # doctest: +SKIP
Roessler — 3-D continuous flow tsdynamics...Rossler
equations dx/dt = -y - z
...
copy
¶
copy() -> SystemBase
Return a deep copy with the same class, params, and ic.
The copy has its own independent params, so mutating the clone's
parameters never affects the original. dim and field_shape are
forwarded, so a system that took them from the constructor copies.
| RETURNS | DESCRIPTION |
|---|---|
SystemBase
|
A fresh instance of the same subclass with copied |
See Also
copy : copy.copy(system) — the same independence, but it keeps
the recorded meta (and does not re-run __init__).
Source code in src/tsdynamics/families/base.py
with_params
¶
with_params(**overrides: Any) -> 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).run(final_time=50)
| PARAMETER | DESCRIPTION |
|---|---|
**overrides
|
New parameter values. Keys must exist in
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
SystemBase
|
New instance of the same subclass. |
Source code in src/tsdynamics/families/base.py
ContinuousSystem
¶
ContinuousSystem(
params: dict[str, Any] | None = None,
ic: Any | None = None,
dim: int | None = None,
field_shape: tuple[int, ...] | None = None,
seed: int | None = None,
**param_kwargs: Any,
)
Bases: SystemBase, ABC
Base class for ODE-based dynamical systems, integrated on the engine.
Subclass contract
- Declare
params = {...}anddim = Nat class level. -
Implement
_equationsas a@staticmethodreturning a length-dimsequence of SymEngine symbolic expressions. Its signature is_equations(y, t, **params), andyis a state ACCESSOR, not an array: componentiisy(i)— a call, which is also what lets a :class:~tsdynamics.families.delay.DelaySystemwrite a delayed access asy(i, t - tau). So neithery[0]norx, y, z = yworks here (aDiscreteMap's_stepdoes take a plain state vector — that is the one place the two families differ)::class MyLorenz(ts.ContinuousSystem): params = {"sigma": 10.0, "rho": 28.0, "beta": 8 / 3} variables = ("x", "y", "z") dim = 3
@staticmethod def _equations(y, t, sigma, rho, beta): x, u, z = y(0), y(1), y(2) # NOT `x, u, z = y` return [sigma * (u - x), x * (rho - z) - u, x * u - beta * z] -
Optionally mark integer or loop-structural parameters in
_structural_params— these are baked into the lowered tape rather than exposed as runtime control parameters.
Lowering
Each system is lowered once to an in-process IR tape, then JIT-compiled on first use (both memoised, so neither cost repeats); the engine reads non-structural parameters live from the system on every run, so a parameter change never triggers a re-lowering.
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
The numerical kernel this system integrates with when the caller names
none — the class-level counterpart of run(solver=...). Default
"rk45"; declare "bdf" on a reliably stiff system. Spellings are
normalised, so "RK45" and "dopri5" resolve to the same kernel,
and system.info prints the resolved name.
Examples:
>>> lor = Lorenz()
>>> traj = lor.run(final_time=100, dt=0.01)
>>> t, y = traj.unpack() # the two columns
>>> lor.sigma = 15.0 # change param — zero recompile cost
>>> traj2 = lor.run(final_time=100)
Source code in src/tsdynamics/families/base.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | |
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict[str, Any] | None = None,
solver: str | None = None,
rtol: float = DEFAULT_RTOL,
atol: float = DEFAULT_ATOL,
max_step: float | None = None,
backend: str | None = None,
**unknown: Any,
) -> None
(Re)start the incremental stepper from state u at time t.
solver= is the numerical kernel, spelled the same way run spells
it (method= selects an estimator in v6) — and it is refused here
with the same message run gives, because a keyword that teaches at
one door and raises a bare TypeError at the next has taught nothing.
| 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:
|
solver
|
Stepper configuration, as in :meth:
TYPE:
|
rtol
|
Stepper configuration, as in :meth:
TYPE:
|
atol
|
Stepper configuration, as in :meth:
TYPE:
|
max_step
|
Stepper configuration, as in :meth:
TYPE:
|
backend
|
Stepper configuration, as in :meth:
TYPE:
|
Notes
A reinit that raises leaves the system exactly as it was — self.ic
is rolled back rather than left holding the initial condition that failed.
Source code in src/tsdynamics/families/continuous.py
step
¶
Advance the system by n_or_dt and return the new state.
n_or_dt is a time increment, in the same unit as final_time;
omitting it advances by _default_step_dt (0.01, the number every
continuous family uses). A map counts iterations instead — that is
the only thing the word means differently anywhere.
The first call performs an implicit :meth:reinit. Parameter changes
made after reinit take effect on the next reinit, not on a
live stepper.
Notes
Each call advances exactly one dt from the live (state, t),
returning byte-for-byte the trajectory the released per-dt path
produced — there is no batching, so the numbers are unchanged for every
method, adaptive or fixed-step (streams WS-STEPBUF, WS-INVHOIST,
WS-STEPPER). The amortisation is durable: the first step after a
:meth:reinit builds an opaque resumable engine handle
(:class:tsdynamics._rust.OdeStepper, via
:func:~tsdynamics._engine.run.make_ode_stepper) that owns the built tape
evaluator + solver once and carries the live (u, t) across calls; every
later step is one :func:~tsdynamics._engine.run.step_advance on that
handle — the tape is never re-marshalled into the engine again. So a
constant-dt stepping loop (Poincaré refinement, basins over flows) skips
not only the solver-registry resolve, the implicit-Jacobian decision, the
output-grid build, provenance assembly and the :class:Trajectory wrap that
the full :meth:run entry point pays, but also the per-step tape
re-marshalling and tape rebuild the pre-handle stepping core paid. The
control-parameter vector is still read live each step, so the live-stepper
semantics are unchanged.
Why this stays answer-exact: the engine handle's advance(dt) re-seeds a
fresh solver and state for each dt segment (the adaptive controller is
re-seeded each step, exactly as the released per-dt integrate_dense
did), so the numbers are bit-for-bit identical to that path — verified in
the engine's own test. A batch-ahead variant that integrated a whole chunk
in one engine call was rejected (WS-STEPBUF): a chunked adaptive integration
is not equal to N single-dt integrations (the controller would carry
its step/error state across output nodes), which silently corrupted
sensitive consumers such as lyapunov_spectrum. The durable handle amortises
the build/marshalling, never the numerics.
Source code in src/tsdynamics/families/continuous.py
691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 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 | |
state
¶
state() -> ndarray
Return a copy of the current state (implicit reinit if cold).
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
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).
This is the only route to the Jacobian as algebra, which is why it
keeps a slot on a 19-name tab surface (CONTRACT.md §11, T2).
:meth:jacobian evaluates ∂f/∂y to numbers at one state; this hands
back the expressions, with the control parameters still symbols — so you
can read the coupling structure off the matrix, take a further
derivative, substitute a parameter to see where a term vanishes, or
export the equations to another tool. Nothing else in the library
exposes them: the engine consumes the lowered tape, and every analysis
consumes numbers.
| RETURNS | DESCRIPTION |
|---|---|
list of ``dim`` rows, each a list of ``dim`` SymEngine expressions.
|
|
See Also
jacobian : the same matrix evaluated numerically at (u, t).
Examples:
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
rhs
¶
Evaluate the right-hand side :math:f(u, t) — the vector field itself.
The function :meth:jacobian is the derivative of. It had no public
door until v6: system.jacobian(u, t) was public while the field it
differentiates was reachable only as system._rhs_numeric(), which is
a strange split — and a costly one, because three of the plotting
layer's transforms (vector_field, flow_speed, streamlines)
are this function, so a user had no sanctioned way to check the
picture against the maths. Two independent readers and a blind tester
reached for the private name; that is the definition of a missing door.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
State at which to evaluate the field.
TYPE:
|
t
|
Time (it matters only for a non-autonomous system). Default
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim))
|
:math: |
See Also
jacobian : The matrix of this function's partial derivatives.
Examples:
>>> import numpy as np, tsdynamics as ts
>>> lor = ts.systems.Lorenz()
>>> np.round(lor.rhs([1.0, 1.0, 1.0]), 6)
array([ 0. , 26. , -1.666667])
The Jacobian is this field's derivative, and a finite difference of one proves the other::
>>> u = np.array([1.0, 2.0, 3.0])
>>> h, col = 1e-6, 1
>>> step = np.zeros(3); step[col] = h
>>> numeric = (lor.rhs(u + step) - lor.rhs(u - step)) / (2 * h)
>>> bool(np.allclose(numeric, lor.jacobian(u)[:, col], atol=1e-6))
True
Source code in src/tsdynamics/families/continuous.py
run
¶
run(
final_time: float = 100.0,
dt: float | None = None,
*,
t0: float = 0.0,
ic: Any | None = None,
transient: float = 0.0,
solver: str | None = None,
rtol: float = DEFAULT_RTOL,
atol: float = DEFAULT_ATOL,
max_step: float | None = None,
backend: str | None = None,
seed: int | None = None,
events: Any = None,
**solver_options: Any,
) -> Trajectory
Integrate the flow and return a :class:~tsdynamics.families.Trajectory.
run is the trajectory verb — one word on flows, maps, delay and
stochastic systems. integrate and trajectory were two more names
for this method and are gone in v6.
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
End of the integration window, in time units — the horizon word
for a flow. (A map counts iterations instead and takes
TYPE:
|
dt
|
Output sampling interval, in time units — the spacing of the
returned grid. .. versionchanged:: 6.0
Before v6 the stepper was forced to land on every output sample,
so a fine
TYPE:
|
t0
|
Where the integration starts, in time units. 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:
|
solver
|
Solver name, resolved by the solver registry (default
TYPE:
|
rtol
|
Solver tolerances — the accuracy knob. Default
:data: .. versionchanged:: 6.0
Tightened from
TYPE:
|
atol
|
Solver tolerances — the accuracy knob. Default
:data: .. versionchanged:: 6.0
Tightened from
TYPE:
|
max_step
|
Upper bound on any single internal solver step, in time units.
TYPE:
|
backend
|
Where the ODE is integrated. Defaults to
.. versionchanged:: 6.0
The default moved from
TYPE:
|
seed
|
Seed for the random initial-condition draw — the same meaning it
has on :meth: .. versionadded:: 6.0
TYPE:
|
events
|
Detect events along the flow (the SciPy-shaped
TYPE:
|
transient
|
Leading stretch of the run to discard, in time units (the same
unit as .. versionadded:: 6.0
Only the retired
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
A container of samples: |
Source code in src/tsdynamics/families/continuous.py
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 | |
DelaySystem
¶
DelaySystem(
params: dict[str, Any] | None = None,
ic: Any | None = None,
dim: int | None = None,
field_shape: tuple[int, ...] | None = None,
seed: int | None = None,
**param_kwargs: Any,
)
Bases: SystemBase, ABC
Base class for delay differential systems (DDEs), integrated on the engine.
Subclass contract
- Declare
params = {...}anddim = N. - Implement
_equationsas a@staticmethodreturning a length-dimsequence of SymEngine symbolic expressions. Usey(i, t - tau)for delayed state access.
Lowering
Each system is lowered once to an in-process IR tape, then JIT-compiled on first use (both memoised, so neither cost repeats). Delay values directly affect the history-buffer structure, so they are baked into the tape rather than read live like the other parameters; a delay change re-lowers, while ordinary parameters are read live with no re-lowering.
Tolerances
The DDE family keeps its own, looser default —
:data:~tsdynamics._utils.tolerances.DDE_RTOL /
:data:~tsdynamics._utils.tolerances.DDE_ATOL (1e-3 / 1e-3) — rather
than the ODE :data:~tsdynamics._utils.tolerances.DEFAULT_RTOL /
:data:~tsdynamics._utils.tolerances.DEFAULT_ATOL. The reason is not that
the solver struggles when tightened (it does not: all six built-in DDEs
complete at rtol=1e-12/atol=1e-15). It is that the tolerance is
largely inert here: the method of steps lands on every output sample, so
dt already bounds the internal step below the natural error. Measured
over all six built-in DDEs at the default dt=0.02 to T=10, five
return a bit-identical final state at rtol=1e-3 and at
rtol=1e-9; the sixth (IkedaDelay, the only one whose step is
tolerance-bound) costs 1.4x for a 6x accuracy gain. Tighten it explicitly if
you need that.
.. versionchanged:: 6.0
The ODE default tightened to 1e-9/1e-12 to compensate for native
dense output. DDEs never had dense output — the march always landed on
every sample — so they lost nothing and this default is unchanged.
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.
lyapunov_spectrum starts from a constant past, so the workaround is
to run integrate first with the desired history, then pass the
end-state as ic to lyapunov_spectrum.
Examples:
>>> import tsdynamics as ts
>>> mg = MackeyGlass()
>>> hist = lambda s: [1.0 + 0.1 * np.sin(0.2 * s)]
>>> traj = mg.run(final_time=500, history=hist)
>>> exps = ts.analysis.lyapunov_spectrum(mg, k=2, ic=traj.y[-1])
Source code in src/tsdynamics/families/base.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | |
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict[str, Any] | None = None,
rtol: float | None = None,
atol: float | None = None,
backend: str | None = None,
**unknown: Any,
) -> 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, call :meth:run with history= and continue from
traj.y[-1].
Stepping is forward-only and re-integrates from the constant past on the
Rust DDE engine each call (the method of steps has no stateful one-step
restart), so it is correct but O(steps²) — use :meth:run for a full
trajectory.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
The constant past to restart from —
TYPE:
|
t
|
Must be
TYPE:
|
params
|
Parameter overrides applied to this system in place.
TYPE:
|
rtol
|
Tolerances for the per-step march. Default
:data:
TYPE:
|
atol
|
Tolerances for the per-step march. Default
:data:
TYPE:
|
backend
|
Which evaluator drives the per-step march — the same word
:meth:
TYPE:
|
Source code in src/tsdynamics/families/delay.py
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 390 391 392 393 394 395 396 397 398 399 400 401 402 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 | |
step
¶
Advance by n_or_dt time units and return the new state (forward-only).
n_or_dt is a time increment here, as on every continuous family;
omitting it advances by _default_step_dt (0.01, the same number
:class:~tsdynamics.families.ContinuousSystem and
:class:~tsdynamics.families.StochasticSystem use — it was 0.1, a
difference no mathematics asked for). A map counts iterations instead.
Source code in src/tsdynamics/families/delay.py
run
¶
run(
final_time: float = 100.0,
dt: float | None = None,
*,
ic: Any | None = None,
history: History = None,
transient: float = 0.0,
solver: str | None = None,
rtol: float | None = None,
atol: float | None = None,
backend: str | None = None,
seed: int | None = None,
**solver_options: Any,
) -> Trajectory
Integrate the delay system and return a :class:~tsdynamics.families.Trajectory.
run is the trajectory verb — one word on flows, maps, delay and
stochastic systems. integrate and trajectory were two more names
for this method and are gone in v6.
The signature is closed. Before v6 this method accepted — and
silently dropped — max_step, t0, events and outright typos:
the run completed, the number was wrong, and nothing said so.
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
End of the integration window, in time units — the horizon word for a flow. Default 100.0.
TYPE:
|
dt
|
Output sampling interval, in time units.
TYPE:
|
ic
|
The constant past, used when
TYPE:
|
history
|
The past, as a function of the lag: A constant past sitting exactly on a fixed point has no dynamics to grow, which is why the Lyapunov exponents of such a run are ~0.
TYPE:
|
rtol
|
Integration tolerances. Default
:data:
TYPE:
|
atol
|
Integration tolerances. Default
:data:
TYPE:
|
backend
|
Which evaluator drives the DDE engine. Defaults to
Only constant delays lower; a state-dependent delay raises.
.. versionchanged:: 6.0
The default moved from
TYPE:
|
solver
|
The explicit kernel (
TYPE:
|
seed
|
Seed for the random initial-condition draw (the constant past when
.. versionadded:: 6.0
TYPE:
|
transient
|
Leading stretch of the run to discard, in time units (the same
unit as .. versionadded:: 6.0
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
|
Source code in src/tsdynamics/families/delay.py
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 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 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 | |
DiscreteMap
¶
DiscreteMap(
params: dict[str, Any] | None = None,
ic: Any | None = None,
dim: int | None = None,
field_shape: tuple[int, ...] | None = None,
seed: int | None = None,
**param_kwargs: Any,
)
Bases: SystemBase, ABC
Base class for discrete maps iterated on the engine.
Subclass contract
- Declare
params = {...}anddim = N. - Implement
_stepas a@staticmethod. Parameters arrive as positional arguments in the order they appear in the class-levelparamsdict. It is :func:abc.abstractmethodand — since this class is an :class:abc.ABC, like every other family base — a subclass that omits it cannot be instantiated. (Before v6DiscreteMapwas the one family base that was not an ABC, so the abstract marker was inert and a missing kernel surfaced far downstream as a mystifyingTapeCompileErrorfrom the lowering pass.) _jacobianis optional: by default the library differentiates_stepsymbolically, exactly asContinuousSystemautogenerates its Jacobian from_equations. Write one only when the step cannot be traced (a Pythonifon the state) or when a one-sided slope on a discontinuity is the meaningful answer. Every catalogue map still ships a hand-written one, which the test suite uses as a cross-check oracle.
Iteration
iterate lowers _step to an in-process IR tape and runs the engine's
native map loop, on a tape lowered and JIT-compiled once per system (both
memoised). The engine reads the current parameter
values live on every run, so a parameter change never triggers a
re-lowering.
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.run(steps=10_000)
>>> t_idx, X = traj.unpack() # the two columns
>>> exps = h._lyapunov_spectrum(n=5_000)
>>> h_variant = h.with_params(a=1.2)
>>> traj2 = h_variant.run(steps=10_000)
Source code in src/tsdynamics/families/base.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | |
jacobian
¶
Return the tangent map J = df/dx at state u.
The same verb, the same signature and the same return type as a flow's
:meth:~tsdynamics.families.ContinuousSystem.jacobian — a map simply
has no t dependence, so the argument is accepted and ignored.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
The state to linearise at.
TYPE:
|
t
|
Ignored; present so a caller holding any system can write
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim, dim))
|
|
Examples:
Source code in src/tsdynamics/families/discrete.py
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict[str, Any] | None = None,
**unknown: Any,
) -> None
(Re)start stepping from state u at iteration count t.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
The state to restart from —
TYPE:
|
t
|
The iteration count to restart the clock at, in iterations (a map has no time). Default 0.
TYPE:
|
params
|
Parameter overrides applied to this system in place before the
restart. (
TYPE:
|
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
For any other keyword — refused by name. This used to end in a
bare |
Source code in src/tsdynamics/families/discrete.py
step
¶
Advance n_or_dt iterations and return the new state.
n_or_dt is an iteration count here — the one family where it is
not a time increment, because a map has no clock. The first call
performs an implicit :meth:reinit.
| PARAMETER | DESCRIPTION |
|---|---|
n_or_dt
|
Number of iterations to advance (default 1). Must be a positive whole number — a discrete map has no notion of a fractional step.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim))
|
A copy of the state after |
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If |
ConvergenceError
|
If the orbit diverges to a non-finite state within the |
Source code in src/tsdynamics/families/discrete.py
time
¶
time() -> int
Return the current iteration count — an int, because it counts.
A flow's clock is a float time; a map's is a whole number of iterates,
and reporting 7.0 where the docstring promised a count made the two
readings of time() indistinguishable at a call site.
Source code in src/tsdynamics/families/discrete.py
run
¶
run(
steps: int = 1000,
*,
ic: Any | None = None,
transient: int = 0,
backend: str | None = None,
seed: int | None = None,
max_retries: int = 10,
**solver_options: Any,
) -> Trajectory
Iterate the map and return a :class:~tsdynamics.families.Trajectory.
run is the trajectory verb — one word on flows, maps, delay and
stochastic systems. iterate and trajectory were two more names
for this method and are gone in v6.
A map's horizon is steps, a count of iterations, because a map
has no continuous time; final_time is refused by name rather than
reinterpreted. The positional form reads the same on both families:
hen.run(1000), lor.run(100.0).
| PARAMETER | DESCRIPTION |
|---|---|
steps
|
How many iterations to run — the horizon word for a map, counted
in iterations. (A flow measures its horizon in time and takes
TYPE:
|
ic
|
Initial state —
TYPE:
|
max_retries
|
Retry with a new random IC if divergence is detected — only when the
initial condition was not chosen by the user. An explicit
TYPE:
|
seed
|
Seed for the random-IC fallback and for the divergence retries, so an
unseeded-IC run is reproducible. Equivalent to the constructor's
TYPE:
|
backend
|
Where the iteration runs. Defaults to
.. versionchanged:: 6.0
The default moved from Every backend lowers
TYPE:
|
transient
|
Leading stretch of the orbit to discard, in iterations (the same
unit as .. versionadded:: 6.0
Only the retired
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
.. versionchanged:: 6.0
The initial condition used to be dropped, so |
| RAISES | DESCRIPTION |
|---|---|
ConvergenceError
|
If an explicit |
EngineNotAvailableError
|
If a Rust-engine backend ( |
TapeCompileError
|
If |
InvalidParameterError
|
If |
Source code in src/tsdynamics/families/discrete.py
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 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 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 | |
StochasticSystem
¶
StochasticSystem(
params: dict[str, Any] | None = None,
ic: Any | None = None,
dim: int | None = None,
field_shape: tuple[int, ...] | None = None,
seed: int | None = None,
**param_kwargs: Any,
)
Bases: SystemBase, ABC
Base class for diagonal-Itô stochastic differential equations.
Subclass contract
- Declare
params = {...}anddim = Nat class level. -
Implement
_driftand_diffusionas@staticmethods, each returning a length-dimsequence of SymEngine symbolic expressions (usey(i)for componentiandtfor time — no NumPy, nomath, no Pythonif): -
_drift(y, t, **params)is the deterministic partf; -
_diffusion(y, t, **params)is the per-component diagonal noise coefficientg(sodX_k = f_k dt + g_k dW_k). -
Optionally mark integer / loop-structural parameters in
_structural_params(baked in at lowering time, like the ODE family).
Example
Geometric Brownian motion dX = μX dt + σX dW::
class GeometricBrownianMotion(StochasticSystem):
params = {"mu": 0.1, "sigma": 0.3}
dim = 1
variables = ("x",)
@staticmethod
def _drift(y, t, mu, sigma):
return [mu * y(0)]
@staticmethod
def _diffusion(y, t, mu, sigma):
return [sigma * y(0)]
gbm = GeometricBrownianMotion()
traj = gbm.run(final_time=1.0, dt=0.01, ic=[1.0], seed=0)
Notes
Integration uses a fixed step (the step is the noise scale √dt).
method selects "euler_maruyama" (default, order 0.5) or "milstein"
(order 1.0). Pass seed for a reproducible noise realisation; the resolved
seed is recorded in the trajectory's meta.
Source code in src/tsdynamics/families/base.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 | |
rhs
¶
Return the drift f(u, t) — the deterministic skeleton's field.
The twin of :meth:jacobian, and named on the same principle: an SDE has
two kernels, so "the right-hand side" is ambiguous by itself, and this
one is the drift — the field whose Jacobian :meth:jacobian returns and
whose equilibria the deterministic analyses find. The diffusion is not a
vector field over (u, t) alone (it multiplies dW), so it has no
place here.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
TYPE:
|
t
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim))
|
|
Examples:
Source code in src/tsdynamics/families/stochastic.py
jacobian
¶
Return the drift Jacobian d f/d u at state u.
An SDE has two kernels, so "the Jacobian" is ambiguous by itself; this one is the drift's, which is the object linear stability of the deterministic skeleton is read from. The diffusion's own derivative (Milstein's correction) is built by the lowering, not exposed here.
| PARAMETER | DESCRIPTION |
|---|---|
u
|
TYPE:
|
t
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(dim, dim))
|
|
Examples:
Source code in src/tsdynamics/families/stochastic.py
reinit
¶
reinit(
u: Any | None = None,
*,
t: float | None = None,
params: dict[str, Any] | None = None,
solver: str | None = None,
seed: int | None = None,
dt: float | None = None,
**unknown: Any,
) -> 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:
|
solver
|
TYPE:
|
seed
|
Seed for the noise stream (random if omitted) — set it for a reproducible path.
TYPE:
|
dt
|
Default step size for :meth:
TYPE:
|
Source code in src/tsdynamics/families/stochastic.py
step
¶
Advance by n_or_dt and return the new state.
n_or_dt is a time increment, in the same unit as final_time,
and for an SDE it is also the noise scale (the increment is drawn
~ N(0, dt)); omitting it uses the stepper's dt — the dt= given
to :meth:reinit, else _default_step_dt (0.01, the number every
continuous family uses).
The first call performs an implicit :meth:reinit. Each call draws a
fresh diagonal Wiener increment from the stepper's seeded stream, so
repeated stepping traces one reproducible sample path.
Source code in src/tsdynamics/families/stochastic.py
set_state
¶
set_state(u: Any) -> None
Overwrite the current state without changing the current time.
Unlike a DDE (whose state is a whole history function), an SDE's state is
a single Markovian point, so set_state is well-defined.
Source code in src/tsdynamics/families/stochastic.py
run
¶
run(
final_time: float = 100.0,
dt: float | None = None,
*,
t0: float = 0.0,
ic: Any | None = None,
transient: float = 0.0,
solver: str | None = None,
seed: int | None = None,
backend: str | None = None,
**solver_options: Any,
) -> Trajectory
Integrate the SDE and return a :class:~tsdynamics.families.Trajectory.
run is the trajectory verb — one word on flows, maps, delay and
stochastic systems. integrate and trajectory were two more names
for this method and are gone in v6.
There is no rtol/atol here: an Ito scheme takes fixed dt
steps and carries no embedded error estimate, so dt is both the
discretisation and the output grid.
| PARAMETER | DESCRIPTION |
|---|---|
final_time
|
End of the integration window, in time units — the horizon word for a flow.
TYPE:
|
dt
|
Fixed step size and output sampling interval, in time units.
For an SDE the step is the noise scale (each increment is drawn
TYPE:
|
t0
|
Where the integration starts, in time units (the IC is the state
at
TYPE:
|
ic
|
Initial state at
TYPE:
|
solver
|
TYPE:
|
seed
|
Makes the whole run reproducible: it seeds the noise realisation
and the random initial-condition draw. (Every other family has
only the second source of randomness, so there .. note::
TYPE:
|
backend
|
Defaults to .. versionchanged:: 6.0
The default moved from
TYPE:
|
transient
|
Leading stretch of the path to discard, in time units (the same
unit as .. versionadded:: 6.0
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
Trajectory
|
A container of samples: |
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If an adaptive-solver keyword ( |
Source code in src/tsdynamics/families/stochastic.py
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 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 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 | |
System
¶
Bases: Protocol
Structural type for steppable dynamical systems.
run
¶
run(*args: Any, **kwargs: Any) -> Trajectory
Produce a :class:~tsdynamics.families.Trajectory.
The one trajectory verb on every family and every wrapper. The horizon
keyword follows the family (final_time for a flow, steps for a
map); everything family-independent — ic, seed, backend,
transient — is spelled identically everywhere.
Source code in src/tsdynamics/families/protocol.py
step
¶
Advance the system and return the new state.
The argument is the number of iterations for a discrete map and the time
increment dt for a continuous flow (each family supplies a sensible
default when None). Calling step on a fresh system performs an
implicit :meth:reinit first.
| RETURNS | DESCRIPTION |
|---|---|
ndarray
|
The state after advancing. |
Source code in src/tsdynamics/families/protocol.py
state
¶
state() -> ndarray
Return a copy of the current state vector.
Calling state on a fresh system performs an implicit :meth:reinit
first. The returned array is a copy, so mutating it never disturbs the
live stepper.