Reference
Top level¶
tsdynamics.<TAB> shows seventeen names. That is not minimalism for its own
sake — it is one rule applied mechanically:
A public name is a verb you call on the thing you are already holding, a fact about that thing, or a member of exactly one named registry. Everything else still exists — importable, reachable, tested — it just stops shouting.
import tsdynamics as ts
traj = ts.systems.Lorenz().run(final_time=100.0, dt=0.01)
exps = ts.analysis.lyapunov_spectrum(ts.systems.Henon())
ts.plot(traj, color="crimson", title="Lorenz")
The seventeen¶
BackendError DiscreteMap StochasticSystem plot
ContinuousSystem InvalidInputError StepBudgetError systems
ConvergenceError InvalidParameterError TSDynamicsError viz
DelaySystem Trajectory WrappedSystem __version__
analysis
Five classes you subclass · one type you receive and annotate · one plotting verb
· three registries · six names you type inside except · the version.
| Symbol | What it is | Canonical home |
|---|---|---|
ContinuousSystem |
subclass it to define an ODE | tsdynamics.families.continuous |
DelaySystem |
…a DDE | tsdynamics.families.delay |
DiscreteMap |
…a map | tsdynamics.families.discrete |
StochasticSystem |
…an SDE | tsdynamics.families.stochastic |
WrappedSystem |
…or adapt an external stepper | tsdynamics.families.wrapped |
Trajectory |
what every run returns — and what you build from measured data |
tsdynamics.data |
plot |
the plotting front door (ts.plot is ts.viz.plot) |
tsdynamics.viz |
systems |
the 177 built-in models | — |
analysis |
the 50 quantifiers | — |
viz |
plots, primitives, transforms, themes, renderers | — |
TSDynamicsError … StepBudgetError |
the six exception types you catch | tsdynamics.errors |
__version__ |
the installed version | — |
The five family bases are here because each is the sole spelling of a
capability: there is no verb and no plain-Python path that defines a delay
system for you. Trajectory is the one type you receive that is also a type
you type — you annotate it, you isinstance it, and you construct it
(ts.Trajectory(t, y)) from data measured elsewhere.
The six error classes are here for the same reason: except ConvergenceError: is
something you type, and reaching for ts.errors.ConvergenceError to write a
single except clause is a toll. Each one subclasses the builtin you would
historically have caught, so a RuntimeError / ValueError / TypeError
except keeps catching the same failure.
| You catch | It is a | Raised when |
|---|---|---|
InvalidParameterError |
ValueError |
a bad dt, solver, backend, section, parameter value |
InvalidInputError |
TypeError |
a malformed argument — an array of the wrong shape, the wrong subject |
ConvergenceError |
RuntimeError |
divergence, non-convergence, no section crossing |
StepBudgetError |
a ConvergenceError |
the step budget ran out with a finite state — a settings problem, not a blow-up |
BackendError |
RuntimeError |
the compiled engine is unavailable |
TSDynamicsError |
the base | catch-all for everything the library raises |
Where everything else lives¶
Nothing was deleted. Roughly 230 names are merely demoted — they live at one importable address each, and the error you get from guessing wrong hands you that address (see the ladder below).
| Go here | For | When you would |
|---|---|---|
ts.analysis |
the 50 quantifiers + find + register + results |
always — this is where analyses live |
ts.systems |
the 177 models, plus names() / find() / get() |
ts.systems.Lorenz() |
ts.viz |
Plot, draw, grid, geometry, transforms, primitives, themes, renderers, spec |
you are styling, composing, exporting or extending a plot |
ts.analysis.results |
the 32 result classes | annotating a function that returns one |
ts.viz.spec |
the IR nouns (Layer, Axis, PlotKind, Geometry, T, …) |
writing a renderer or a transform |
ts.derived |
PoincareMap, StroboscopicMap, TangentSystem, ProjectedSystem, Ensemble |
rarely — the first two and Ensemble have verbs on the system |
ts.data |
Trajectory, Box, Ball, Grid, region, sampler, grid_points, set_distance |
rarely — every region argument takes plain bounds |
ts.errors |
the same six, plus MovedInV6 and the warning types |
reading the hierarchy |
ts.registry |
the system / analysis / renderer / transform registries | enumerating a catalogue programmatically |
ts.families |
SystemBase, ParamSet, the System protocol |
writing a new family, not a new system |
ts.utils |
make_output_grid, the tolerance constants |
reading what a default actually is |
ts.engine / ts.solvers |
the Rust-facing compile/run seam, the solver table | debugging the engine |
The derived views have verbs¶
Two derived systems are built by a verb on the system they wrap, because they speak the same vocabulary the class does:
import numpy as np
lor = ts.systems.Lorenz()
states = np.random.default_rng(0).normal(size=(8, 3))
lor.poincare("y", 0.0) # a PoincareMap (also ("y", 0.0, "up"))
ts.systems.Duffing().poincare(period=4.488) # a strobe — the same verb
band = lor.ensemble(states) # an Ensemble — a system holding 8 copies
band.run(final_time=5.0).final # ...run it → the (8, 3) array of end states
poincare takes either a plane or a period, never both: a plane is an
affine surface \(g(\mathbf u) = \mathbf n\cdot\mathbf u - c\), a period samples the
phase circle, and they are different sections of the same flow.
The two that are machinery rather than everyday verbs live at their address:
from tsdynamics.derived import TangentSystem, ProjectedSystem
TangentSystem(lor, k=2) # the Lyapunov engine, steppable
ProjectedSystem(lor, (0, 2)) # the (x, z) shadow, as a live system
The analyses are free functions¶
An analysis is not a method. It is a free function whose first argument is the thing it is about — a system, a trajectory, or a result:
ts.analysis.lyapunov_spectrum(lor) # a property of the equations
ts.analysis.correlation_dimension(traj) # a property of a point set
ts.analysis.kaplan_yorke_dimension(exps) # a property of the answer above
Pressing ++tab++ on a system therefore shows 19 names, every one a verb or a
fact about that system, and ts.analysis.<TAB> shows the 50 quantifiers, flat
and sorted. ts.analysis.find(subject_or_query) is the search:
ts.analysis.find(traj) # what can I measure on THIS?
ts.analysis.find("is this chaotic") # who answers THIS question?
Regions take plain bounds¶
Everything that asks for a region reads one (lo, hi) pair per state
component — add a third entry, (lo, hi, n), to say how finely to grid it.
No library type is ever required:
vdp, henon = ts.systems.VanDerPol(), ts.systems.Henon()
ts.analysis.fixed_points(vdp, region=[(-3, 3), (-3, 3)])
ts.analysis.basins(henon, [(-2, 2, 20), (-2, 2, 20)], max_steps=500)
ts.analysis.expansion_entropy(henon, [(-1.6, 1.6), (-0.5, 0.5)], n_samples=100, n=8)
A ts.data.Box / Ball / Grid is still accepted at every one of those doors.
It is simply never the price of entry.
The 177 built-in systems¶
Built-in system classes live under tsdynamics.systems —
the canonical path is ts.systems.Lorenz (flat, no need to remember whether a
model is continuous or discrete). ts.Lorenz no longer resolves: the
listing above is the whole top level, and a name that is not in it does not
answer. What you get instead is its address (below). The classes are documented
on their generated pages under Systems, and discoverable
programmatically through the registry or ts.systems.find(...).
The error ladder — how you find a name that moved¶
Curation hides ~230 reachable names from autocomplete, so the error message is the discovery mechanism, and it is built to be one. A miss is answered in five ordered cases.
1 – 3. An exact hit in a redirect table raises MovedInV6 — a subclass of
ImportError, deliberately, because from tsdynamics import X discards a module
__getattr__'s message when it is an AttributeError and keeps it verbatim when
it is an ImportError. Both spellings therefore teach:
>>> ts.Lorenz
tsdynamics.errors.MovedInV6: ts.Lorenz moved in v6: the top level is 17 names now,
and this one lives at its own address.
ts.systems.Lorenz()
>>> from tsdynamics import PlotSpec
tsdynamics.errors.MovedInV6: ts.PlotSpec was renamed in v6. Same capability, one
spelling:
ts.viz.Plot
(it is the type ts.plot hands back, and nothing about it is a spec any more — same
class, renamed, nothing wrapped)
4. A near miss on a name that does exist gets suggestions:
>>> ts.lyapunov
AttributeError: module 'tsdynamics' has no attribute 'lyapunov'. Did you mean:
ts.analysis.lyapunov_spectrum
ts.analysis.lyapunov_from_data
5. A genuine miss names the two catalogues worth tab-completing:
>>> ts.random_typo
AttributeError: module 'tsdynamics' has no attribute 'random_typo'.
Tab-complete a registry, or search:
ts.systems.<TAB> # the 177 built-in systems
ts.analysis.<TAB> # the 50 quantifiers
ts.analysis.find('random_typo') # ...or search by what it does
Cases 4 and 5 stay AttributeError, so hasattr(ts, "anything") is still
False for a name nobody has ever used, and dir(),
inspect.getmembers() and from tsdynamics import * are untouched.
Renamed or removed in v6¶
| Was | Now | Why |
|---|---|---|
sys.integrate(...) / sys.iterate(...) / sys.trajectory(...) |
sys.run(...) |
one verb produces data, on every family |
run(method="rk45") |
run(solver="rk45") |
solver= picks a kernel, method= picks an estimator |
sys.is_discrete |
sys.family |
"ode" / "dde" / "map" / "sde" — four answers a boolean could not give |
sys.stroboscope(period=T) |
sys.poincare(period=T) |
one section verb, two kinds of section |
sys.project(...) / sys.tangent(...) / sys.copies(...) |
ts.derived.ProjectedSystem / TangentSystem / sys.ensemble |
not everyday verbs; copies was one word from ensemble |
sys.lyap.spectrum(), traj.dims.correlation(), … |
ts.analysis.<name>(subject) |
an analysis has one door, and it is the one that cannot hide a sampling choice |
result.summary() |
repr(result) |
the good text already existed inside summary(), which nothing advertised |
PlotSpec |
Plot |
same class — escalating from easy to expert is not a type change |
ts.basins_of_attraction / ts.find_attractors |
ts.analysis.basins / ts.analysis.attractors |
the nouns a user types |
ts.bifurcation_diagram |
ts.analysis.orbit_diagram |
one implementation cannot name two spellings in an error message |
ts.basins (the top-level function) |
deleted — ts.analysis.basins |
it was a function shadowing a subpackage: one word, two objects |