Reference
Analysis¶
The quantifiers that consume any System. Prose-first
treatments live in the Analysis section.
Orbit diagrams¶
orbit_diagram
¶
orbit_diagram(
system: Any,
param: str,
values: Any,
*,
n: int = 200,
transient: int = 500,
carry_state: bool = True,
component: int | str | tuple[Any, ...] = 0,
ic: Any | None = None,
seed: int | None = None,
) -> OrbitDiagram
Sweep a parameter and record the asymptotic orbit at each value.
Works on anything discrete: a :class:~tsdynamics.families.DiscreteMap
directly, or a flow wrapped in a
:class:~tsdynamics.derived.PoincareMap /
:class:~tsdynamics.derived.StroboscopicMap — in which case this is
the bifurcation diagram of the flow. ODE parameter changes reuse the
compiled module (control parameters), so flow sweeps stay cheap; DDE
sweeps recompile per value (their structure depends on all parameters).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
The system to sweep. Never mutated — each value gets a fresh
TYPE:
|
param
|
Parameter name to sweep.
TYPE:
|
values
|
Parameter values, in sweep order.
TYPE:
|
n
|
Points recorded per parameter value.
TYPE:
|
transient
|
Steps discarded before recording, at every value.
TYPE:
|
carry_state
|
Start each value from the previous value's final state (follows the
attractor branch; the classic way to draw clean diagrams). When
False, every value starts from
TYPE:
|
component
|
Which state component(s) to record (names allowed when the system
declares
TYPE:
|
ic
|
Initial state for the first value (and every value when
TYPE:
|
seed
|
Seed for the random initial condition when the system has none; makes the diagram reproducible.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
OrbitDiagram
|
The swept |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
ValueError
|
If a named |
| WARNS | DESCRIPTION |
|---|---|
RuntimeWarning
|
When a parameter value diverges; that value records an empty set and the sweep continues. |
References
May, R. M. (1976). Simple mathematical models with very complicated dynamics. Nature, 261, 459--467.
Examples:
>>> od = orbit_diagram(Logistic(), "r", np.linspace(2.5, 4.0, 600), n=120)
>>> x, y = od.flat()
>>> # bifurcation diagram of a flow:
>>> od = orbit_diagram(PoincareMap(Rossler(), (1, 0.0)), "c", np.linspace(2, 6, 80))
Source code in src/tsdynamics/analysis/orbits/orbit_diagram.py
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 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 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 | |
OrbitDiagram
dataclass
¶
OrbitDiagram(
param: str = "",
values: ndarray = (lambda: empty(0))(),
points: list[ndarray] = list(),
components: tuple[int, ...] = (),
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Result of :func:orbit_diagram.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam. Iterate to
get (value, points) pairs, or use :meth:flat for the scatter-ready
arrays.
flat
¶
Flatten to scatter-plot arrays (x, y).
x repeats each parameter value once per recorded point; y is
the chosen recorded component.
Source code in src/tsdynamics/analysis/orbits/orbit_diagram.py
periods
¶
Return the detected period at each swept parameter value.
Counts the distinct asymptotic branches in the recorded orbit — the
period of a periodic window — by clustering the points of one component
with a scale-free gap test: a new branch starts where the sorted-value
gap exceeds rtol times the orbit's range. A finite period p >= 2
is only reported when the recorded iterate sequence actually revisits
its values cyclically (v[i] ≈ v[i + p] to rtol); a chaotic band
whose finite-sample points merely cluster into p bins fails this
repeat test and is reported as aperiodic (0).
| PARAMETER | DESCRIPTION |
|---|---|
component
|
Which recorded component to count branches in.
TYPE:
|
max_period
|
Periods above this are reported as
TYPE:
|
rtol
|
Relative gap (fraction of the per-value range) separating branches.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
numpy.ndarray of int
|
One entry per parameter value: the period |
Source code in src/tsdynamics/analysis/orbits/orbit_diagram.py
bifurcation_points
¶
Parameter values where the detected period changes.
Locates the boundaries of the period-doubling cascade (and other
bifurcations) as the midpoints between consecutive swept values across
which :meth:periods differs. Transitions touching a diverged value
(-1) are skipped.
| PARAMETER | DESCRIPTION |
|---|---|
component
|
Which recorded component to count branches in.
TYPE:
|
max_period
|
Periods above this are treated as aperiodic when detecting changes.
TYPE:
|
rtol
|
Relative gap separating branches in :meth:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
numpy.ndarray of float
|
Estimated bifurcation parameter values, in sweep order. Their
resolution is the spacing of |
References
Feigenbaum, M. J. (1978). Quantitative universality for a class of nonlinear transformations. Journal of Statistical Physics, 19, 25--52.
Source code in src/tsdynamics/analysis/orbits/orbit_diagram.py
to_plot_spec
¶
Describe this orbit diagram as a backend-agnostic :class:PlotSpec.
Builds an ORBIT_DIAGRAM scatter of the asymptotic state (first
recorded component) against the swept parameter — the classic
bifurcation diagram — via :meth:flat.
The default plot is clean (just the scatter of asymptotic states, the
textbook bifurcation picture). A chaotic period-doubling cascade contains
dozens of onsets, so drawing them all as labelled vertical reference
lines smears the figure into an illegible pile of overlapping text. Pass
annotate=True to overlay the :meth:bifurcation_points onsets as
"vline" :class:~tsdynamics.viz.spec.Annotation reference lines (each
labelled with the period it opens onto) — best kept for a short, low-period
sweep where the labels don't collide. The :mod:tsdynamics.viz.spec
import is lazy, so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
annotate
|
Overlay the detected period-doubling onsets as labelled vertical
reference lines. Off by default so the default
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/orbits/orbit_diagram.py
Poincaré sections¶
poincare_section
¶
poincare_section(
system: Any,
plane: tuple[Any, ...],
*,
direction: int | str = +1,
n: int = 1000,
skip_crossings: int = 0,
dt: float = 0.01,
max_time: float = 10000.0,
seed: int | None = None,
) -> PoincareSection
Poincaré surface of section.
Two input modes:
- System → wraps it in a :class:
~tsdynamics.derived.PoincareMapand collectsnroot-refined crossings on the fast Rust event engine (stream WS-CROSSKERNEL). - Trajectory → finds the plane crossings between consecutive samples by linear interpolation (pure data path; accuracy limited by the trajectory's sampling interval).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A flow to section, or measured trajectory data (the
TYPE:
|
plane
|
The section, in any of three spellings:
For example
TYPE:
|
direction
|
Crossing direction filter (
TYPE:
|
n
|
Number of crossings to collect (system mode).
TYPE:
|
skip_crossings
|
Number of leading crossings to discard before recording. (A section
transient is a count of crossings, deliberately distinct from the
time/step
TYPE:
|
dt
|
Detection step and integration ceiling (system mode) — see
:class:
TYPE:
|
max_time
|
Detection step and integration ceiling (system mode) — see
:class:
TYPE:
|
seed
|
Seed for the random initial condition when the system has none (system mode); makes the section reproducible.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PoincareSection
|
A :class: |
Examples:
>>> section = poincare_section(Rossler(), plane=("y", 0.0, "up"), n=500)
>>> section = poincare_section(traj, plane=("z", 25.0)) # from data
Source code in src/tsdynamics/analysis/orbits/poincare.py
Return maps¶
return_map
¶
return_map(
system: Any,
component: int | str = 0,
*,
method: str = "max",
plane: tuple[Any, ...] | None = None,
direction: int = +1,
n: int = 2000,
final_time: float = 200.0,
dt: float = 0.01,
transient: float = 0.0,
skip_crossings: int = 0,
ic: Any | None = None,
seed: int | None = None,
**integrate_kwargs: Any,
) -> ReturnMap
First-return map of a recurring observable.
Records a sequence of scalar values :math:v_0, v_1, \dots from the motion
and pairs each with its successor, giving the one-dimensional map
:math:v_{n+1} = F(v_n) that organises the dynamics.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
What to read the observable from. A continuous
:class:
TYPE:
|
component
|
Which state component to record (names allowed when the system /
trajectory declares |
method
|
TYPE:
|
plane
|
TYPE:
|
direction
|
Crossing-direction filter (
TYPE:
|
n
|
Number of section crossings to collect when integrating a system in
TYPE:
|
final_time
|
Integration horizon and detection / output step used when
TYPE:
|
dt
|
Integration horizon and detection / output step used when
TYPE:
|
transient
|
Elapsed time discarded before recording extrema (
TYPE:
|
skip_crossings
|
Number of leading crossings discarded before recording
(
TYPE:
|
ic
|
Initial state when
TYPE:
|
seed
|
Seed for the random initial condition when the system has none; makes the map reproducible.
TYPE:
|
**integrate_kwargs
|
Forwarded to
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ReturnMap
|
The recorded |
Examples:
>>> rm = return_map(Lorenz(), "z", method="max", final_time=400.0, transient=40.0)
>>> x, y = rm.flat() # the cusp map z_n -> z_{n+1}
>>> rm = return_map(Rossler(), 0, method="poincare", plane=(0, 0.0), n=400)
Source code in src/tsdynamics/analysis/orbits/return_map.py
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | |
ReturnMap
dataclass
¶
ReturnMap(
current: ndarray = (lambda: empty(0))(),
successor: ndarray = (lambda: empty(0))(),
values: ndarray = (lambda: empty(0))(),
times: ndarray = (lambda: empty(0))(),
observable: int = 0,
kind: str = "max",
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Result of :func:return_map.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam. The recorded
observable values are :attr:values; the return map itself is the pair
(:attr:current, :attr:successor) = :math:(v_n, v_{n+1}). Iterate for
(current, successor) pairs, or use :meth:flat for the scatter-ready
arrays.
flat
¶
to_plot_spec
¶
Describe this return map as a backend-agnostic :class:PlotSpec.
Builds a RETURN_MAP scatter of :math:(v_n, v_{n+1}) with the
diagonal :math:v_{n+1} = v_n drawn as a reference line (its fixed-point
locus). The :mod:tsdynamics.viz.spec import is lazy, so building a spec
never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/orbits/return_map.py
cobweb
¶
Describe the return map's cobweb (staircase) as a :class:PlotSpec.
The cobweb diagram traces the iteration :math:v_{n+1} = F(v_n) as a
staircase: from a point on the diagonal it steps vertically to the return
curve, then horizontally back to the diagonal, and repeats. This emits a
COBWEB spec carrying the scatter of the return points
:math:(v_n, v_{n+1}), the diagonal :math:v_{n+1} = v_n, and the
staircase LINE itself built from the recorded sequence. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls
a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/orbits/return_map.py
Lyapunov quantifiers¶
lyapunov_spectrum
¶
lyapunov_spectrum(
system: Any,
*,
k: int | None = None,
final_time: float | None = None,
n: int | None = None,
transient: float | None = None,
dt: float | None = None,
ic: Any | None = None,
method: str | None = None,
) -> LyapunovSpectrum
Lyapunov spectrum of any system — the uniform, documented entry point.
Dispatches to the family implementation (QR tangent dynamics for maps, the extended variational system on the engine for ODEs, the engine function-space estimator for DDEs), translating this one signature to each family's native keywords. The exponents are obtained by Benettin renormalisation of an evolving orthonormal frame (Benettin et al. 1980).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A flow (ODE/DDE) or a discrete map.
TYPE:
|
k
|
Number of exponents to compute (was
TYPE:
|
final_time
|
Averaging-window length for a flow (after the transient). Mutually
exclusive with
TYPE:
|
n
|
Number of iterations for a map. Mutually exclusive with
TYPE:
|
transient
|
Amount discarded before averaging (a flow burn-in time). Maps reorthonormalise from the initial condition and take no transient here.
TYPE:
|
dt
|
Sampling / integration step (flows only).
TYPE:
|
ic
|
Initial condition. Falls back to
TYPE:
|
method
|
Solver kernel (continuous flows only).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
LyapunovSpectrum
|
The exponents (largest first), a drop-in for the bare |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
ValueError
|
If |
Examples:
>>> lyapunov_spectrum(Lorenz(), final_time=300.0) # [0.91, ~0, -14.57]
>>> lyapunov_spectrum(Henon(), k=2, n=5000) # [0.42, -1.62]
References
G. Benettin, L. Galgani, A. Giorgilli & J.-M. Strelcyn, "Lyapunov characteristic exponents for smooth dynamical systems and for Hamiltonian systems; a method for computing all of them", Meccanica 15 (1980) 9--20 (Part 1) and 21--30 (Part 2).
Source code in src/tsdynamics/analysis/lyapunov/__init__.py
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | |
max_lyapunov
¶
max_lyapunov(
system: Any,
*,
d0: float = 1e-09,
n: int = 400,
steps_per: int = 5,
dt: float | None = None,
transient: int = 500,
ic: Any | None = None,
seed: int | None = None,
) -> ScalarResult
Maximal Lyapunov exponent by two-trajectory rescaling (Benettin et al. 1976).
Runs a reference and a perturbed copy of the system in lockstep through
the :class:~tsdynamics.families.System protocol — no Jacobian needed, so it
works for any ODE or map (including ones with non-smooth right-hand
sides). The separation is rescaled back to d0 after every cycle and the
accumulated :math:\ln(d / d_0) is averaged over elapsed time. Not
available for DDEs (their state cannot be set_state-ed); use
DelaySystem.lyapunov_spectrum instead.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
ODE or map.
TYPE:
|
d0
|
Perturbation size restored at every rescaling.
TYPE:
|
n
|
Number of rescaling cycles (more → better averaging).
TYPE:
|
steps_per
|
Protocol steps between rescalings.
TYPE:
|
dt
|
Step size for continuous systems (default: the system's step default).
TYPE:
|
transient
|
Protocol steps discarded before measuring.
TYPE:
|
ic
|
Initial condition for the reference trajectory.
TYPE:
|
seed
|
Seed for the random perturbation direction (two-trajectory path) and for the off-basin random-IC retry on the map engine-kernel path, so a result that triggers the retry is reproducible.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ScalarResult
|
Estimated maximal exponent (per unit time / per iteration), a drop-in for
its |
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
If |
ValueError
|
If |
ConvergenceError
|
If the two trajectories collapse or diverge (zero / non-finite separation), or a continuous system's clock does not advance. |
Examples:
References
G. Benettin, L. Galgani & J.-M. Strelcyn, "Kolmogorov entropy and numerical experiments", Physical Review A 14 (1976) 2338--2345.
Source code in src/tsdynamics/analysis/lyapunov/__init__.py
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 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 | |
lyapunov_from_data
¶
lyapunov_from_data(
data: ndarray,
*,
dt: float = 1.0,
dimension: int = 3,
delay: int = 1,
theiler: int | None = None,
k_max: int = 20,
eps: float | None = None,
n_neighbors: int = 1,
method: str = "kantz",
fit: tuple[int, int] | None = None,
) -> LyapunovFromData
Estimate the maximal Lyapunov exponent from a time series.
Reconstructs an m-dimensional delay embedding of series, measures how
fast nearby trajectories diverge as a function of the look-ahead k, and
reads the exponent off the slope of the resulting log-divergence curve.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
1-D scalar series, or 2-D
TYPE:
|
dt
|
Sampling interval (time between consecutive samples). Use
TYPE:
|
dimension
|
Embedding dimension. Choose it from the data — large enough to unfold the attractor (e.g. a false-nearest-neighbour estimate); too small underestimates the exponent.
TYPE:
|
delay
|
Embedding delay, in samples. For oversampled flows pick it near the first minimum of the mutual information / first zero of the autocorrelation.
TYPE:
|
theiler
|
Theiler window (Theiler 1986): neighbours with
TYPE:
|
k_max
|
Number of forward samples over which divergence is tracked; the curve
spans
TYPE:
|
eps
|
Neighbour-ball radius for
TYPE:
|
n_neighbors
|
Minimum neighbours a reference point needs to contribute (Kantz only).
TYPE:
|
method
|
Divergence estimator (see module docstring).
TYPE:
|
fit
|
Inclusive sample range |
| RETURNS | DESCRIPTION |
|---|---|
LyapunovFromData
|
The estimated exponent, the full divergence curve, and the parameters
used. Casts to |
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If a reconstruction parameter is invalid ( |
ConvergenceError
|
If no usable neighbour is found (no reference point has a neighbour
within |
Notes
The estimate is only as good as the embedding and the chosen scaling region.
Always look at result.times vs result.divergence: a trustworthy
estimate comes from a clear straight segment before the curve saturates.
Examples:
>>> import tsdynamics as ts
>>> traj = ts.Henon().trajectory(6000, transient=500, ic=[0.1, 0.1])
>>> res = ts.lyapunov_from_data(traj.y[:, 0], dimension=4, k_max=12, fit=(0, 6))
>>> 0.30 < float(res) < 0.55 # ≈ 0.42
True
References
H. Kantz, "A robust method to estimate the maximal Lyapunov exponent of a time series", Physics Letters A 185 (1994) 77--87.
M. T. Rosenstein, J. J. Collins & C. J. De Luca, "A practical method for calculating largest Lyapunov exponents from small data sets", Physica D 65 (1993) 117--134.
Source code in src/tsdynamics/analysis/lyapunov/from_data.py
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 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 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 | |
LyapunovFromData
dataclass
¶
LyapunovFromData(
estimate: float = 0.0,
stderr: float = 0.0,
abscissa: ndarray = (lambda: empty(0))(),
ordinate: ndarray = (lambda: empty(0))(),
fit_region: tuple[int, int] = (0, 0),
intercept: float = 0.0,
embedding_dim: int = 0,
delay: int = 0,
theiler: int = 0,
n_reference: int = 0,
method: str = "kantz",
*,
meta: Mapping[str, Any] = dict(),
)
Bases: ScalingResult
Outcome of :func:lyapunov_from_data: the divergence curve and its slope.
A :class:~tsdynamics.analysis._result.ScalingResult — the maximal Lyapunov
exponent is read off the slope of the stretching curve, the same shape every
fractal dimension and embedding diagnostic share — so it inherits the canonical
estimate / abscissa / ordinate / fit_region schema, the result
surface (.meta / .summary() / .to_dict() / the .plot seam) and
float(result) (the exponent). Domain-named @property aliases
(:attr:lyapunov, :attr:times, :attr:divergence) preserve the original
field names.
| ATTRIBUTE | DESCRIPTION |
|---|---|
estimate |
Estimated maximal Lyapunov exponent (per unit time), the slope of
TYPE:
|
abscissa |
Relative times
TYPE:
|
ordinate |
The stretching curve
TYPE:
|
fit_region |
Inclusive index range into the curve used for the slope. |
embedding_dim, delay, theiler |
Reconstruction parameters actually used.
TYPE:
|
n_reference |
Number of reference points that contributed (had a usable neighbour).
TYPE:
|
method |
TYPE:
|
lyapunov
property
¶
lyapunov: float
The estimated maximal Lyapunov exponent (alias of :attr:estimate).
to_plot_spec
¶
Describe the divergence curve as a backend-agnostic :class:PlotSpec.
Builds a SCALING_FIT spec — the stretching curve :math:S(k) (mean
log-divergence) against time as a scatter, the fitted scaling region
highlighted, and the line of slope :attr:lyapunov drawn over it — the
same schema the fractal-dimension estimators emit, so a single
result.plot.scaling() renders it. The :mod:tsdynamics.viz.spec
import is lazy, so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/lyapunov/from_data.py
kaplan_yorke_dimension
¶
kaplan_yorke_dimension(spectrum: Any) -> ScalarResult
Kaplan--Yorke (Lyapunov) dimension from a Lyapunov spectrum.
.. math::
D_{KY} = j + \frac{\lambda_1 + \cdots + \lambda_j}{|\lambda_{j+1}|},
where :math:j is the largest index whose cumulative exponent sum is
non-negative (Kaplan & Yorke 1979). Interpolating between the :math:j-th
and :math:(j+1)-th exponents gives a fractional estimate of the attractor's
information dimension.
| PARAMETER | DESCRIPTION |
|---|---|
spectrum
|
Lyapunov exponents (any order; sorted descending internally).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ScalarResult
|
The dimension, a drop-in for its |
Examples:
References
J. L. Kaplan & J. A. Yorke, "Chaotic behavior of multidimensional difference equations", in Functional Differential Equations and Approximation of Fixed Points, Lecture Notes in Mathematics 730, Springer (1979) 204--227.
Source code in src/tsdynamics/analysis/lyapunov/__init__.py
Fixed points & periodic orbits¶
fixed_points
¶
fixed_points(
system: Any,
*,
region: Any = None,
n_seeds: int = 200,
tol: float = 1e-12,
max_iter: int = 60,
dedup_tol: float = 1e-06,
method: str = "newton",
lam: float = 0.05,
beta: float = 1.0,
max_c: int | None = None,
seed: int | None = None,
) -> FixedPointSet
Find fixed points of a map (f(x) = x) or equilibria of a flow (f(x) = 0).
Seeds are drawn uniformly from region plus points sampled from a short
orbit; each runs the chosen root finder, and converged roots are deduplicated
and classified by the Jacobian spectrum (maps: |lambda| < 1; flows:
Re lambda < 0).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A discrete map (fixed points) or a continuous flow (equilibria). Delay and stochastic systems are not supported.
TYPE:
|
region
|
Search region; defaults to a burn-in orbit's bounding box padded by 50 %,
or
TYPE:
|
n_seeds
|
Random seeds (orbit points are added on top).
TYPE:
|
tol
|
Residual tolerance (
TYPE:
|
max_iter
|
Root-finding iterations per seed.
TYPE:
|
dedup_tol
|
Distance below which two roots are merged.
TYPE:
|
method
|
TYPE:
|
lam
|
Step size of the Schmelcher--Diakonos iteration (
TYPE:
|
beta
|
Regularisation strength of the Davidchack--Lai iteration
(
TYPE:
|
max_c
|
Cap on the number of stabilising matrices tried (
TYPE:
|
seed
|
RNG seed for the multi-start sampling. All randomness (the box seeds and
the burn-in orbit's starting state) is drawn from a local
:class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
FixedPointSet
|
A list-like |
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
If |
ValueError
|
If |
InvalidInputError
|
If |
Examples:
>>> fixed_points(Henon()) # two saddles of the Hénon map
>>> fixed_points(Lorenz()) # the origin and the two C± equilibria
>>> fixed_points(Henon(), region=([-3, -3], [3, 3]), method="interval") # rigorous
References
Schmelcher & Diakonos (1997), Phys. Rev. Lett. 78, 4733. Davidchack & Lai (1999), Phys. Rev. E 60, 6172. Krawczyk (1969), Computing 4, 187. Neumaier (1990), Interval Methods for Systems of Equations, CUP.
Source code in src/tsdynamics/analysis/fixedpoints/fixed.py
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 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 436 437 438 | |
FixedPoint
dataclass
¶
FixedPoint(
x: ndarray = (lambda: empty(0))(),
eigenvalues: ndarray = (lambda: empty(0))(),
stable: bool = False,
continuous: bool = False,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A fixed point (map) or equilibrium (flow) with its linear stability data.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam alongside its
point and stability data.
| ATTRIBUTE | DESCRIPTION |
|---|---|
x |
The point, shape
TYPE:
|
eigenvalues |
Eigenvalues of the Jacobian at
TYPE:
|
stable |
For a map,
TYPE:
|
continuous |
TYPE:
|
to_plot_spec
¶
Describe this fixed point as a backend-agnostic :class:PlotSpec.
Builds a FIXED_POINTS_OVERLAY: a single SCATTER point at the first
two coordinates of :attr:x, styled by stability (a filled marker for a
stable point, an open marker for an unstable one). Designed to be drawn
over a phase portrait via :meth:AnalysisResult.overlay_on, which keeps
the host layers first. For the eigenvalue/multiplier picture use
:meth:eigenvalue_plane. The :mod:tsdynamics.viz.spec import is lazy,
so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (a :class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/fixedpoints/fixed.py
eigenvalue_plane
¶
Describe the Jacobian spectrum as an :class:EIGENVALUE_PLANE spec.
Plots the eigenvalues / multipliers of :attr:eigenvalues in the complex
plane. The stability boundary is drawn as a reference geometry: the unit
circle for a map (|λ| = 1) or the imaginary axis for a flow
(Re λ = 0), per :attr:continuous. The :mod:tsdynamics.viz.spec
import is lazy, so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/fixedpoints/fixed.py
periodic_orbits
¶
periodic_orbits(
system: Any,
period: int,
*,
region: Any = None,
n_seeds: int = 300,
method: str = "dl",
lam: float = 0.05,
beta: float = 1.0,
tol: float = 1e-12,
max_iter: int = 200,
dedup_tol: float = 1e-06,
prime: bool = True,
max_c: int | None = None,
seed: int | None = None,
) -> OrbitSet
Find period-period orbits of a discrete map.
Solves :math:f^{p}(x) = x by multi-start root finding (Davidchack--Lai by
default — the stabilising transformations reach unstable orbits that plain
Newton misses), recovers each orbit by forward iteration, filters orbits
whose minimal period properly divides period (prime=True), and merges
the cyclic shifts of one orbit.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
The map.
TYPE:
|
period
|
The period
TYPE:
|
region
|
Seeding controls (see :func:
TYPE:
|
n_seeds
|
Seeding controls (see :func:
TYPE:
|
dedup_tol
|
Seeding controls (see :func:
TYPE:
|
seed
|
Seeding controls (see :func:
TYPE:
|
method
|
Root finder.
TYPE:
|
lam
|
Stabilising-transformation controls (see
TYPE:
|
beta
|
Stabilising-transformation controls (see
TYPE:
|
max_c
|
Stabilising-transformation controls (see
TYPE:
|
tol
|
Residual tolerance
TYPE:
|
max_iter
|
Iterations per seed/matrix.
TYPE:
|
prime
|
Keep only orbits of minimal period
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
OrbitSet
|
A list-like |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
ValueError
|
If |
Examples:
>>> periodic_orbits(Logistic(params={"r": 3.2}), 2) # the stable 2-cycle
>>> periodic_orbits(Logistic(params={"r": 3.83}), 3) # stable node + saddle
Source code in src/tsdynamics/analysis/fixedpoints/periodic.py
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 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 | |
periodic_orbit
¶
periodic_orbit(
system: Any,
*,
ic: Any | None = None,
period_guess: float | None = None,
steps_per_period: int = 2000,
transient: float = 0.0,
tol: float = 1e-10,
max_iter: int = 50,
n_points: int = 400,
min_amplitude: float = 1e-06,
seed: int | None = None,
) -> PeriodicOrbit
Find a periodic orbit of an autonomous flow by single shooting.
Newton iterates the unknowns (x0, T) to solve φ_T(x0) − x0 = 0 with an
orthogonality phase condition f(x0)·δx = 0 (which removes the trivial
time-shift degeneracy). The monodromy matrix M = dφ_T/dx0 comes from
integrating the variational equation alongside the state; the Floquet
multipliers eig(M) give stability (one trivial multiplier ≈ 1).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
An autonomous flow.
TYPE:
|
ic
|
Initial guess for a point on the orbit (default: the system's IC, after a short burn-in if it is not already near the cycle).
TYPE:
|
period_guess
|
Initial guess for the period
TYPE:
|
steps_per_period
|
Fixed RK4 sub-steps used to integrate one period (state + monodromy).
TYPE:
|
transient
|
Time to forward-integrate
TYPE:
|
tol
|
Convergence tolerance on
TYPE:
|
max_iter
|
Maximum Newton iterations.
TYPE:
|
n_points
|
Number of points sampled along the converged orbit.
TYPE:
|
min_amplitude
|
Minimum orbit extent (bounding-box diagonal) for the result to count as a
genuine cycle. Shooting can collapse onto the trivial solution
TYPE:
|
seed
|
Seed for the burn-in IC, when
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PeriodicOrbit
|
With |
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
If |
ValueError
|
If |
RuntimeError
|
If the Newton iteration does not converge, or it collapses onto an
equilibrium (the target may be a centre — a non-isolated orbit — rather
than a hyperbolic cycle); try a better |
Examples:
Source code in src/tsdynamics/analysis/fixedpoints/periodic.py
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 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 | |
PeriodicOrbit
dataclass
¶
PeriodicOrbit(
points: ndarray = (lambda: empty(0))(),
period: int | float = 0,
multipliers: ndarray = (lambda: empty(0))(),
stable: bool = False,
continuous: bool = False,
residual: float = 0.0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A periodic orbit with its Floquet/multiplier stability data.
| ATTRIBUTE | DESCRIPTION |
|---|---|
points |
The orbit, shape
TYPE:
|
period |
The (minimal) period — an integer iteration count for a map, the time
|
multipliers |
Stability multipliers: eigenvalues of :math:
TYPE:
|
stable |
TYPE:
|
continuous |
TYPE:
|
residual |
Closure residual
TYPE:
|
to_plot_spec
¶
Describe this periodic orbit as a backend-agnostic :class:PlotSpec.
Builds a phase portrait of the orbit :attr:points: a closed LINE3D
loop for a 3-D-or-higher flow cycle (first three coordinates), a 2-D
LINE (flow) / SCATTER (the discrete map cycle's distinct points)
for two coordinates, and a 1-D index plot for a scalar map. A flow loop
is drawn as a line (the continuous cycle); a map orbit as markers (its
period distinct points). The :mod:tsdynamics.viz.spec import is
lazy, so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (a :class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/fixedpoints/periodic.py
eigenvalue_plane
¶
Describe the multiplier spectrum as an :class:EIGENVALUE_PLANE spec.
Plots the stability multipliers in the complex plane against the unit
circle (a map's eigenvalues of :math:Df^{p}, or a flow's Floquet
multipliers, both judged by |μ| < 1). For a flow the trivial
multiplier ≈ 1 along the flow direction is split into its own
distinctly-marked layer — located here for the plot by argmin|μ − 1|
(a presentation heuristic; the stability flag itself uses the more robust
eigenvector-alignment test). The :mod:tsdynamics.viz.spec import is
lazy.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/fixedpoints/periodic.py
estimate_period
¶
estimate_period(
data: Any,
*,
dt: float | None = None,
component: int | str | None = None,
method: str = "autocorrelation",
max_delay: int | None = None,
detrend: bool = True,
) -> ScalarResult
Estimate the dominant period of a sampled signal.
Accepts a :class:~tsdynamics.data.Trajectory (the sampling step is read from
its time grid), a 1-D array, or a 2-D array (one row per sample); for
multi-component input, component selects the channel (default: the
highest-variance one).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The signal.
TYPE:
|
dt
|
Sampling step. For a bare array it sets the time unit (default
TYPE:
|
component
|
Channel to analyse for multi-component input. |
method
|
TYPE:
|
max_delay
|
Largest lag considered (
TYPE:
|
detrend
|
Subtract the mean before estimating (default
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ScalarResult
|
The estimated period in time units ( |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If fewer than 8 samples are given, the signal is constant, the
autocorrelation is degenerate or has no zero-crossing, the spectrum has
no dominant frequency, or |
Examples:
References
Box, G. E. P. & Jenkins, G. M. (1970). Time Series Analysis: Forecasting and Control. Holden-Day (autocorrelation method).
Source code in src/tsdynamics/analysis/fixedpoints/periodic.py
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 | |
Chaos indicators¶
Three literature-validated answers to "is this orbit chaotic?": the Generalized Alignment Index (GALI), the 0--1 test, and Hunt--Ott expansion entropy.
gali
¶
gali(
system: Any,
k: int = 2,
*,
n: int | None = None,
final_time: float | None = None,
dt: float | None = None,
ic: Any | None = None,
transient: float | None = None,
seed: int | None = 0,
n_internal: int = 10,
) -> GALIResult
Compute the GALI\ :sub:k time series of a map or flow.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
The system whose tangent dynamics to track. Delay/stochastic systems are not supported (their tangent space is not finite-dimensional here).
TYPE:
|
k
|
Number of deviation vectors,
TYPE:
|
n
|
Number of iterations (maps). Default 1000.
TYPE:
|
final_time
|
Integration time (flows). Default 100.0.
TYPE:
|
dt
|
Sampling/recording step for flows. Default 0.1. Not valid for maps.
TYPE:
|
ic
|
Initial condition (defaults to the system's resolved IC).
TYPE:
|
transient
|
Burn-in discarded before tracking (iterations for maps, time for flows). Defaults: 500 iterations / 20.0 time units.
TYPE:
|
seed
|
Seed for the random orthonormal initial deviation frame.
TYPE:
|
n_internal
|
RK4 sub-steps per
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
GALIResult
|
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
If |
InvalidInputError
|
If an explicit |
InvalidParameterError
|
If |
ConvergenceError
|
Only when |
Notes
For a chaotic orbit GALI\ :sub:k decays as
:math:\exp\!\big[-\sum_{i=2}^{k}(\lambda_1-\lambda_i)\,t\big], so its
log-slope measures the leading Lyapunov-exponent gaps; a regular orbit keeps
it bounded. See the module docstring for the full statement.
Examples:
>>> gali(Henon(), k=2, n=60).is_chaotic() # exponential collapse
True
>>> float(gali(Lorenz(), k=2, final_time=25.0)) # ~0: Lorenz is chaotic
0.0...
References
Skokos, Bountis & Antonopoulos, "Geometrical properties of local dynamics in Hamiltonian systems: The Generalized Alignment Index (GALI) method", Physica D 231 (2007) 30--54.
Source code in src/tsdynamics/analysis/chaos/gali.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 | |
GALIResult
dataclass
¶
GALIResult(
k: int = 0,
times: ndarray = (lambda: empty(0))(),
values: ndarray = (lambda: empty(0))(),
is_discrete: bool = False,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A GALI\ :sub:k time series with the tools to read order vs chaos off it.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam.
float(result) is the final value — :math:\approx 1 for a regular orbit,
:math:\to 0 for a chaotic one — so the result drops straight into a
threshold test.
| ATTRIBUTE | DESCRIPTION |
|---|---|
k |
Number of deviation vectors.
TYPE:
|
times |
Time (flows) or iteration index (maps) at each sample.
TYPE:
|
values |
GALI\ :sub:
TYPE:
|
is_discrete |
Whether the underlying system is a map.
TYPE:
|
final
property
¶
final: float
The last GALI value.
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If the series is empty (no samples were recorded). |
decay_rate
¶
Exponential decay rate of GALI\ :sub:k (a positive number for chaos).
Fits a line to :math:\ln \mathrm{GALI}_k over the samples that lie
above floor (i.e. before the index reaches the floating-point noise
floor) and returns -slope. For a chaotic orbit this estimates the
Lyapunov gap sum :math:(\lambda_1-\lambda_2)+\cdots+(\lambda_1-\lambda_k)
(Skokos et al. 2008); for a regular orbit it is ~0.
| PARAMETER | DESCRIPTION |
|---|---|
floor
|
Ignore samples at or below this value (numerical underflow region).
TYPE:
|
t_min
|
Also ignore samples before this time/iteration (skip the initial alignment transient).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
Estimated decay rate; |
Source code in src/tsdynamics/analysis/chaos/gali.py
is_chaotic
¶
to_plot_spec
¶
Describe the GALI\ :sub:k curve as a backend-agnostic :class:PlotSpec.
Builds a DIAGNOSTIC_CURVE of GALI\ :sub:k against time (or iteration
index for a map) on a log y-axis — the recommended scale, since
GALI\ :sub:k decays exponentially for a chaotic orbit and saturates for
a regular one, so a log axis reads the decay rate off as a slope. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls a
plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/chaos/gali.py
zero_one_test
¶
zero_one_test(
system: Any,
*,
component: int | None = None,
final_time: float | None = None,
n: int | None = None,
dt: float | None = None,
transient: float | None = None,
ic: Any | None = None,
n_c: int = 100,
c_range: tuple[float, float] = (
pi / 5.0,
4.0 * pi / 5.0,
),
n_cut: int | None = None,
seed: int | None = 0,
return_distribution: bool = False,
) -> ZeroOneResult | tuple[ZeroOneResult, ndarray]
Run the 0--1 test for chaos on a system or a measured observable.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A dynamical system (integrated / iterated internally to produce the
observable, like :func:
TYPE:
|
component
|
Column to use when a multi-component system / trajectory is passed.
TYPE:
|
final_time
|
Integration horizon for a flow (system input). Default 1000.0.
TYPE:
|
n
|
Number of iterations for a map / discrete view (system input). Default 5000.
TYPE:
|
dt
|
Sampling / integration step for a flow (system input). Default 0.1.
TYPE:
|
transient
|
Discarded before recording — a flow burn-in time, a map / discrete burn-in in steps (system input).
TYPE:
|
ic
|
Initial condition (system input).
TYPE:
|
n_c
|
Number of random frequencies :math:
TYPE:
|
c_range
|
Interval the frequencies are drawn from. The default avoids the
resonances near :math: |
n_cut
|
Largest displacement lag used in the mean-square displacement. Default
TYPE:
|
seed
|
Seed for the frequency draw (makes :math:
TYPE:
|
return_distribution
|
If true, also return the per-frequency :math:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ZeroOneResult or (ZeroOneResult, ndarray)
|
The median correlation growth indicator :math: |
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If the observable is shorter than 200 points (too short for the test to
be meaningful); if |
Examples:
>>> zero_one_test(Logistic(params={"r": 4.0}), n=5000) > 0.9 # chaotic
True
>>> x = Logistic(params={"r": 4.0}).iterate(steps=5000).component("x")
>>> zero_one_test(x) > 0.9 # the data overload
True
References
Gottwald & Melbourne, "A new test for chaos in deterministic systems", Proc. R. Soc. Lond. A 460 (2004) 603--611.
Gottwald & Melbourne, "On the implementation of the 0--1 test for chaos", SIAM J. Appl. Dyn. Syst. 8 (2009) 129--145.
Source code in src/tsdynamics/analysis/chaos/zero_one.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | |
expansion_entropy
¶
expansion_entropy(
system: Any,
region: Any = None,
*,
n_samples: int = 1000,
n: int | None = None,
final_time: float | None = None,
dt: float | None = None,
fit_range: tuple[int, int] | None = None,
seed: int | None = 0,
n_internal: int = 5,
) -> ExpansionEntropyResult
Estimate the expansion entropy :math:H of a map or flow on a region.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
The system whose expansion to measure.
TYPE:
|
region
|
The restricting region :math:
TYPE:
|
n_samples
|
Number of initial conditions sampled uniformly in the region.
TYPE:
|
n
|
Number of iterations (maps). Default 15. (Kept modest: the raw tangent product is not renormalised, so very long horizons overflow.)
TYPE:
|
final_time
|
Integration time (flows). Default 5.0.
TYPE:
|
dt
|
Recording step for flows. Default 0.1. Not valid for maps.
TYPE:
|
fit_range
|
Inclusive index range in the :math: |
seed
|
Seed for the initial-condition sampling.
TYPE:
|
n_internal
|
RK4 sub-steps per
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ExpansionEntropyResult
|
|
| RAISES | DESCRIPTION |
|---|---|
NotImplementedError
|
If |
InvalidParameterError
|
If |
Examples:
References
Hunt & Ott, "Defining chaos", Chaos 25 (2015) 097618.
Source code in src/tsdynamics/analysis/chaos/expansion.py
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | |
ExpansionEntropyResult
dataclass
¶
ExpansionEntropyResult(
estimate: float = 0.0,
stderr: float = 0.0,
abscissa: ndarray = (lambda: empty(0))(),
ordinate: ndarray = (lambda: empty(0))(),
fit_region: tuple[int, int] = (0, 0),
intercept: float = 0.0,
n_samples: int = 0,
n_survivors: int = 0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: ScalingResult
An expansion-entropy estimate with the growth curve it was read from.
A :class:~tsdynamics.analysis._result.ScalingResult — the entropy is the
slope of :math:\ln E(t) against :math:t — so it inherits the canonical
estimate / abscissa / ordinate / fit_region schema, the result
surface (.meta / .summary() / .to_dict() / the .plot seam) and
float(result) (the entropy :math:H). Domain-named @property aliases
(:attr:entropy, :attr:times, :attr:log_growth, :attr:fit_slice)
preserve the original field names.
| ATTRIBUTE | DESCRIPTION |
|---|---|
estimate |
The estimated expansion entropy :math:
TYPE:
|
abscissa |
The :math:
TYPE:
|
ordinate |
:math:
TYPE:
|
fit_region |
Inclusive |
n_samples |
Number of initial conditions sampled in the region.
TYPE:
|
n_survivors |
How many of them stayed in the region for the whole run.
TYPE:
|
Entropy & complexity¶
Composable estimation — an OutcomeSpace
(how a series is symbolised), a probability estimator, and an information
measure — plus the named measures built on it.
entropy
¶
entropy(
data: Any,
*,
outcomes: OutcomeSpace,
prob: ProbabilityEstimator | None = None,
measure: InformationMeasure | None = None,
normalize: bool = False,
component: int | str | None = None,
) -> ScalarResult
Entropy of data — the composable entry point.
Symbolise with outcomes, estimate probabilities with prob, then
apply the information measure. With normalize=True the result is
divided by the measure's maximum over the outcome alphabet, mapping it to
[0, 1].
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The series (see :func:
TYPE:
|
outcomes
|
Symbolisation scheme (e.g. :class:
TYPE:
|
prob
|
Defaults to :class:
TYPE:
|
measure
|
Defaults to :class:
TYPE:
|
normalize
|
Divide by the maximum (uniform) value over the alphabet.
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
The (optionally normalised) entropy. |
Examples:
>>> import numpy as np
>>> rng = np.random.default_rng(0)
>>> float(entropy(rng.random(5000), outcomes=OrdinalPatterns(3), normalize=True)) # ≈ 1
0.99...
Source code in src/tsdynamics/analysis/entropy/core.py
permutation_entropy
¶
permutation_entropy(
data: Any,
dimension: int = 3,
delay: int = 1,
*,
base: float = 2.0,
normalize: bool = True,
component: int | str | None = None,
) -> ScalarResult
Permutation entropy of a time series (Bandt & Pompe 2002).
The Shannon entropy of the distribution of ordinal patterns — the relative
rankings within every length-dimension embedded window. It is invariant
under any strictly monotonic transform of the data and needs no amplitude
thresholds, which makes it a robust complexity measure for noisy
experimental series.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Scalar time series (or a component of a multivariate one).
TYPE:
|
dimension
|
Ordinal (embedding) order. Typical choices are
TYPE:
|
delay
|
Embedding delay.
TYPE:
|
base
|
Logarithm base (
TYPE:
|
normalize
|
Divide by
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
Permutation entropy. |
Examples:
>>> permutation_entropy(np.arange(1000)) # monotone → 0
0.0
>>> rng = np.random.default_rng(0)
>>> float(permutation_entropy(rng.random(10000))) # white noise → ≈ 1
0.99...
Source code in src/tsdynamics/analysis/entropy/permutation.py
weighted_permutation_entropy
¶
weighted_permutation_entropy(
data: Any,
dimension: int = 3,
delay: int = 1,
*,
base: float = 2.0,
normalize: bool = True,
component: int | str | None = None,
) -> ScalarResult
Weighted permutation entropy (Fadlallah et al. 2013).
Like :func:permutation_entropy, but each ordinal pattern is weighted by the
variance of its embedding window before the probabilities are formed. This
restores sensitivity to amplitude — high-variance (often more dynamically
relevant) windows count more, so abrupt large-amplitude events are no longer
treated like tiny fluctuations sharing the same ordinal pattern.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
As in :func:
TYPE:
|
dimension
|
As in :func:
TYPE:
|
delay
|
As in :func:
TYPE:
|
base
|
As in :func:
TYPE:
|
normalize
|
As in :func:
TYPE:
|
component
|
As in :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
Weighted permutation entropy. |
Source code in src/tsdynamics/analysis/entropy/permutation.py
dispersion_entropy
¶
dispersion_entropy(
data: Any,
c: int = 6,
dimension: int = 2,
delay: int = 1,
*,
base: float = 2.0,
normalize: bool = True,
component: int | str | None = None,
) -> ScalarResult
Dispersion entropy of a time series (Rostaghi & Azami 2016).
The series is mapped through the normal CDF onto c amplitude classes,
embedded with order dimension and delay τ, and the Shannon entropy of
the resulting dispersion patterns is returned. Unlike permutation entropy
it keeps amplitude information and is markedly faster than sample entropy,
while remaining robust to noise.
Notes
When normalize=True the entropy is divided by log_base(c**dimension) —
the log of the full dispersion-pattern alphabet (the canonical Rostaghi &
Azami 2016 normalisation). Because a finite series can populate at most
n − (dimension − 1)·delay of those c**dimension patterns, even ideal
white noise sits slightly below 1 at finite length (it approaches 1 only as
n → ∞); this is expected, not a defect.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Scalar time series (or a component of a multivariate one).
TYPE:
|
c
|
Number of amplitude classes.
TYPE:
|
dimension
|
Embedding order.
TYPE:
|
delay
|
Embedding delay.
TYPE:
|
base
|
Logarithm base (
TYPE:
|
normalize
|
Divide by
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
Dispersion entropy. |
Examples:
>>> rng = np.random.default_rng(0)
>>> float(dispersion_entropy(rng.random(10000))) # white noise → ≈ 1
0.99...
Source code in src/tsdynamics/analysis/entropy/dispersion.py
sample_entropy
¶
sample_entropy(
data: Any,
dimension: int = 2,
r: float | None = None,
delay: int = 1,
*,
component: int | str | None = None,
) -> ScalarResult
Sample entropy (Richman & Moorman 2000).
SampEn = -ln(A / B), where B and A count template pairs (excluding
self-matches) that stay within Chebyshev tolerance r over windows of
length dimension and dimension+1 respectively. Excluding self-matches
removes the bias of approximate entropy, making the statistic largely
independent of series length. Larger values mean less regularity.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Scalar time series (or a component of a multivariate one).
TYPE:
|
dimension
|
Template length.
TYPE:
|
r
|
Tolerance (Chebyshev radius). Defaults to
TYPE:
|
delay
|
Embedding delay.
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
Sample entropy in nats. Returns |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the series is too short or no length- |
References
Richman, J. S. & Moorman, J. R. (2000). Physiological time-series analysis using approximate entropy and sample entropy. Am. J. Physiol. Heart Circ. Physiol. 278, H2039–H2049.
Examples:
>>> rng = np.random.default_rng(0)
>>> float(sample_entropy(rng.random(2000))) # white noise → high
2.2...
Source code in src/tsdynamics/analysis/entropy/sample.py
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
approximate_entropy
¶
approximate_entropy(
data: Any,
dimension: int = 2,
r: float | None = None,
delay: int = 1,
*,
component: int | str | None = None,
) -> ScalarResult
Approximate entropy (Pincus 1991).
ApEn = Φ^m(r) − Φ^{m+1}(r), where Φ^m averages ln C_i^m and
C_i^m is the fraction of length-dimension templates within tolerance
r of template i (including the self-match). Self-matching keeps the
logarithms finite but biases the estimate downward for short series — prefer
:func:sample_entropy when that bias matters.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Scalar time series (or a component of a multivariate one).
TYPE:
|
dimension
|
Template length.
TYPE:
|
r
|
Tolerance (Chebyshev radius). Defaults to
TYPE:
|
delay
|
Embedding delay.
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
Approximate entropy in nats. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the series is too short for the requested |
Notes
The result can be +inf when a template has no within-radius neighbour
other than itself at the (m + 1) dimension (a zero count yields
log(0)), which happens for short or near-deterministic series; widen
r or lengthen the series to obtain a finite estimate.
References
Pincus, S. M. (1991). Approximate entropy as a measure of system complexity. Proc. Natl. Acad. Sci. USA 88, 2297–2301.
Source code in src/tsdynamics/analysis/entropy/sample.py
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 | |
multiscale_entropy
¶
multiscale_entropy(
data: Any,
scales: int | Iterable[int] = 20,
*,
entropy_fn: Callable[..., Any] = sample_entropy,
r: float | None = None,
r_factor: float = 0.15,
component: int | str | None = None,
**kwargs: Any,
) -> ArrayResult
Multiscale entropy: an entropy measured on coarse-grained copies of a series.
For each scale the series is coarse-grained (:func:coarse_grain) and
entropy_fn is applied. Following Costa et al. (2002), when the chosen
entropy takes a tolerance r (e.g. :func:sample_entropy,
:func:approximate_entropy) it is fixed across all scales from the
original series' standard deviation, so the profile reflects structure
rather than the shrinking variance of the coarse-grained signal.
Pure (1/f-like) processes hold a roughly flat profile while uncorrelated white noise decays with scale — the discriminating feature multiscale entropy was designed to expose.
Notes
The default r_factor is 0.15, not the 0.2 that
:func:sample_entropy uses on its own. This is the deliberate multiscale
convention (Costa et al. 2002 sweep r in the 0.1–0.25 · std band and
report 0.15): a tolerance fixed once from the original series keeps the
comparison across scales honest. A direct consequence is that, at defaults,
multiscale_entropy(x)[0] (scale 1) does not equal sample_entropy(x)
— the former uses r = 0.15 · std(x) while the latter uses
0.2 · std(x). Pass r_factor=0.2 (or an explicit r) to make scale 1
coincide with the bare estimator.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
Scalar time series.
TYPE:
|
scales
|
An
TYPE:
|
entropy_fn
|
Single-series entropy applied at each scale. Any of this package's scalar entropies works (sample, approximate, permutation, dispersion).
TYPE:
|
r
|
Explicit tolerance for
TYPE:
|
r_factor
|
When
TYPE:
|
component
|
Component selector for multi-component input. |
**kwargs
|
Forwarded to
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ArrayResult
|
Entropy at each requested scale (same order as |
References
Costa, M., Goldberger, A. L. & Peng, C.-K. (2002). Multiscale entropy analysis of complex physiologic time series. Phys. Rev. Lett. 89, 068102.
Examples:
>>> rng = np.random.default_rng(0)
>>> mse = multiscale_entropy(rng.standard_normal(5000), scales=5)
>>> bool(mse[0] > mse[-1]) # white noise decays with scale
True
Source code in src/tsdynamics/analysis/entropy/multiscale.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | |
lz76_complexity
¶
lz76_complexity(
data: Any,
*,
symbolize: Any = "auto",
threshold: str | float = "median",
normalize: bool = False,
provider: str = "native",
component: int | str | None = None,
) -> ScalarResult
Lempel–Ziv (LZ76) complexity of a sequence.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The sequence. Strings and integer arrays are treated as already
symbolic; a real-valued series is symbolised first (see
TYPE:
|
symbolize
|
How to turn the input into symbols.
TYPE:
|
threshold
|
Threshold used when binarising (see :func:
TYPE:
|
normalize
|
Return the normalised complexity
TYPE:
|
provider
|
TYPE:
|
component
|
Component selector for multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
float
|
The LZ76 complexity (an integer count, or the normalised density when
|
Examples:
>>> float(lz76_complexity("0001101001000101", symbolize=None)) # Kaspar–Schuster
6.0
>>> float(lz76_complexity("aaaaaaaa", symbolize=None)) # constant → 2
2.0
Source code in src/tsdynamics/analysis/entropy/lz.py
lz76_entropy
¶
lz76_entropy(
data: Any,
*,
symbolize: Any = "auto",
threshold: str | float = "median",
provider: str = "native",
component: int | str | None = None,
) -> ScalarResult
LZ76 entropy-rate estimate h ≈ c(S)·log_k(n)/n.
The normalised LZ76 complexity, which converges to the entropy rate of an
ergodic source (Lempel & Ziv 1976). Equivalent to
:func:lz76_complexity with normalize=True. Arguments are as in
:func:lz76_complexity.
| RETURNS | DESCRIPTION |
|---|---|
float
|
Entropy density in units of |
Source code in src/tsdynamics/analysis/entropy/lz.py
Composable building blocks¶
OutcomeSpace
¶
Bases: ABC
A scheme that maps a series to counts over a finite set of outcomes.
Subclasses implement :meth:counts, returning a length-:attr:cardinality
integer vector (zeros for outcomes that never occurred), so downstream
estimators and the normalisation step both know the full outcome alphabet.
OrdinalPatterns
¶
Bases: OutcomeSpace
Ordinal (permutation) patterns of an embedded series (Bandt & Pompe 2002).
Each length-m window (x_i, x_{i+τ}, …, x_{i+(m-1)τ}) is encoded by the
permutation that sorts it — its ordinal pattern. There are m! patterns.
| PARAMETER | DESCRIPTION |
|---|---|
m
|
Embedding (pattern) order,
TYPE:
|
tau
|
Embedding delay,
TYPE:
|
Source code in src/tsdynamics/analysis/entropy/core.py
labels
¶
Return a readable label per ordinal pattern, in dense-index order.
Each label is the rank tuple that sorts a window, rendered compactly
(e.g. "012" for the increasing pattern, "210" for decreasing) —
the category names for a :func:outcome_distribution_plot_spec bar plot.
Falls back to comma separation when the order is m > 9 (ranks become
multi-digit).
| RETURNS | DESCRIPTION |
|---|---|
list of str
|
One label per outcome, length :attr: |
Source code in src/tsdynamics/analysis/entropy/core.py
window_variance
¶
Return (labels, per-window variance) — the weighting for WPE.
Source code in src/tsdynamics/analysis/entropy/core.py
Dispersion
¶
Bases: OutcomeSpace
Dispersion patterns (Rostaghi & Azami 2016).
The series is mapped through the normal CDF (fitted to the sample mean/std)
onto c amplitude classes, then embedded with order m and delay τ;
each window becomes a dispersion pattern over c**m possibilities.
The :attr:cardinality is the full c**m alphabet, so a normalised entropy
(:func:entropy with normalize=True) divides by log(c**m) — the
canonical Rostaghi & Azami (2016) convention. A finite series can occupy at
most n − (m − 1)·τ of those patterns, so even white noise normalises to
slightly below 1 at finite length.
| PARAMETER | DESCRIPTION |
|---|---|
c
|
Number of amplitude classes,
TYPE:
|
m
|
Embedding order,
TYPE:
|
tau
|
Embedding delay,
TYPE:
|
Source code in src/tsdynamics/analysis/entropy/core.py
Shannon
¶
Shannon(base: float = 2.0)
Bases: InformationMeasure
Shannon entropy H = -∑ p log_b p (Shannon 1948).
| PARAMETER | DESCRIPTION |
|---|---|
base
|
Logarithm base
TYPE:
|
References
Shannon, C. E. (1948). A mathematical theory of communication. Bell Syst. Tech. J. 27, 379–423.
Source code in src/tsdynamics/analysis/entropy/core.py
Renyi
¶
Bases: InformationMeasure
Rényi entropy of order q (Rényi 1961).
H_q = (1/(1-q)) log_b ∑ p^q; q → 1 recovers Shannon.
| PARAMETER | DESCRIPTION |
|---|---|
q
|
Order,
TYPE:
|
base
|
Logarithm base.
TYPE:
|
Source code in src/tsdynamics/analysis/entropy/core.py
Tsallis
¶
Tsallis(q: float = 2.0)
Bases: InformationMeasure
Tsallis entropy of order q (Tsallis 1988).
S_q = (1 - ∑ p^q) / (q - 1); q → 1 recovers Shannon (nats).
| PARAMETER | DESCRIPTION |
|---|---|
q
|
Entropic index,
TYPE:
|
Source code in src/tsdynamics/analysis/entropy/core.py
Fractal dimensions¶
correlation_dimension
¶
correlation_dimension(
data: Any,
*,
theiler: int = 0,
metric: str | float = "euclidean",
radii: ndarray | None = None,
n_radii: int = 24,
min_window: int = 5,
tol: float = 1.5,
) -> DimensionResult
Grassberger--Procaccia correlation dimension :math:D_2.
Computes the correlation sum, then reads :math:D_2 off the slope of
:math:\log C(r) vs :math:\log r in the automatically selected scaling
region (:func:~tsdynamics.analysis.dimensions._scaling.fit_scaling_region).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
theiler
|
Theiler window — exclude pairs with :math:
TYPE:
|
metric
|
Distance metric. |
radii
|
Explicit radii; default is a data-adaptive log-spaced grid.
TYPE:
|
n_radii
|
Number of radii when
TYPE:
|
min_window
|
Minimum number of radii in the fitted scaling region.
TYPE:
|
tol
|
Scaling-region residual tolerance (see
:func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DimensionResult
|
|
References
P. Grassberger and I. Procaccia, "Characterization of strange attractors", Phys. Rev. Lett. 50, 346 (1983).
Examples:
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If fewer than :data: |
Source code in src/tsdynamics/analysis/dimensions/correlation.py
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 | |
correlation_sum
¶
correlation_sum(
data: Any,
radii: ndarray | None = None,
*,
theiler: int = 0,
metric: str | float = "euclidean",
n_radii: int = 24,
) -> tuple[ndarray, ndarray]
Correlation sum :math:C(r) over a grid of radii.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set (a :class:
TYPE:
|
radii
|
Radii at which to evaluate :math:
TYPE:
|
theiler
|
Exclude pairs with :math:
TYPE:
|
metric
|
Distance metric ( |
n_radii
|
Number of radii when
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(radii, C) : tuple[ndarray, ndarray]
|
The radii and the corresponding correlation-sum values, normalised so
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the Theiler window leaves no valid pairs. |
Source code in src/tsdynamics/analysis/dimensions/correlation.py
generalized_dimension
¶
generalized_dimension(
data: Any,
q: float = 2.0,
*,
scales: ndarray | None = None,
n_scales: int = 18,
sat_frac: float = 0.85,
min_window: int = 5,
tol: float = 1.5,
offsets: tuple[float, ...] = _DEFAULT_OFFSETS,
) -> DimensionResult
Generalized (Rényi) dimension :math:D_q by box counting.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
q
|
Rényi order.
TYPE:
|
scales
|
Box sizes :math:
TYPE:
|
n_scales
|
Number of box sizes when
TYPE:
|
sat_frac
|
Drop scales whose occupied-box count exceeds
TYPE:
|
min_window
|
Minimum number of box sizes in the fitted scaling region.
TYPE:
|
tol
|
Scaling-region residual tolerance.
TYPE:
|
offsets
|
Grid-origin offsets (as fractions of each box side) swept per scale; the
offset with the fewest occupied boxes (the minimal cover) is kept, which
removes the alignment bias of a single fixed grid origin. Pass
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DimensionResult
|
|
References
H. G. E. Hentschel and I. Procaccia, "The infinite number of generalized dimensions of fractals and strange attractors", Physica D 8, 435 (1983).
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If |
Source code in src/tsdynamics/analysis/dimensions/generalized.py
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | |
box_counting_dimension
¶
box_counting_dimension(
data: Any, **kwargs: Any
) -> DimensionResult
Box-counting (capacity) dimension :math:D_0.
Thin wrapper over :func:generalized_dimension with q=0 — the count of
occupied boxes scales as :math:N(\epsilon) \sim \epsilon^{-D_0}.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
**kwargs
|
Forwarded to :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DimensionResult
|
|
Source code in src/tsdynamics/analysis/dimensions/generalized.py
information_dimension
¶
information_dimension(
data: Any, **kwargs: Any
) -> DimensionResult
Information dimension :math:D_1.
Thin wrapper over :func:generalized_dimension with q=1 — the slope of
the Shannon information :math:\sum_i p_i \log p_i against
:math:\log \epsilon.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
**kwargs
|
Forwarded to :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DimensionResult
|
|
Source code in src/tsdynamics/analysis/dimensions/generalized.py
dimension_spectrum
¶
dimension_spectrum(
data: Any,
qs: Any = None,
*,
scales: ndarray | None = None,
n_scales: int = 18,
sat_frac: float = 0.85,
min_window: int = 5,
tol: float = 1.5,
offsets: tuple[float, ...] = _DEFAULT_OFFSETS,
) -> dict[float, DimensionResult]
Compute the :math:D_q spectrum over several Rényi orders.
Computes box occupancies once per scale and reuses them across every q,
so the whole spectrum costs barely more than a single :math:D_q. For a
monofractal the spectrum is flat; a decreasing :math:D_q signals
multifractality.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
qs
|
Rényi orders. Default:
TYPE:
|
scales
|
Box sizes; default as in :func:
TYPE:
|
n_scales
|
As in :func:
TYPE:
|
sat_frac
|
As in :func:
TYPE:
|
min_window
|
As in :func:
TYPE:
|
tol
|
As in :func:
TYPE:
|
offsets
|
As in :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
dict[float, DimensionResult]
|
|
Source code in src/tsdynamics/analysis/dimensions/generalized.py
fixed_mass_dimension
¶
fixed_mass_dimension(
data: Any,
*,
ks: ndarray | None = None,
theiler: int = 0,
metric: str | float = "euclidean",
n_ref: int | None = 1500,
n_ks: int = 16,
min_window: int = 5,
tol: float = 1.5,
seed: int = 0,
) -> DimensionResult
Fixed-mass (nearest-neighbour) dimension.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The point set.
TYPE:
|
ks
|
Neighbour counts (masses) to probe. Default: a log-spaced integer grid
from 1 to
TYPE:
|
theiler
|
Exclude neighbours with :math:
TYPE:
|
metric
|
Distance metric. |
n_ref
|
Number of reference points averaged over (randomly sub-sampled, seeded).
TYPE:
|
n_ks
|
Number of masses when
TYPE:
|
min_window
|
Minimum number of masses in the fitted scaling region.
TYPE:
|
tol
|
Scaling-region residual tolerance.
TYPE:
|
seed
|
Seed for the reference sub-sample (keeps the estimate reproducible).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DimensionResult
|
|
Notes
The ordinate is the digamma :math:\psi(k) rather than :math:\log k. For
a fixed mass the enclosing radius :math:r_k is a random order statistic, and
:math:\langle\log r_k\rangle = D^{-1}\psi(k) + \text{const} holds without the
:math:O(1/k) bias that :math:\log k carries at small :math:k (Grassberger
1985; the digamma correction of the Kozachenko–Leonenko nearest-neighbour
estimators).
References
R. Badii and A. Politi, "Statistical description of chaotic attractors: The dimension function", J. Stat. Phys. 40, 725 (1985).
P. Grassberger, "Generalizations of the Hausdorff dimension of fractal measures", Phys. Lett. A 107, 101 (1985).
Source code in src/tsdynamics/analysis/dimensions/fixedmass.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
DimensionResult
dataclass
¶
DimensionResult(
estimate: float = 0.0,
stderr: float = 0.0,
abscissa: ndarray = (lambda: empty(0))(),
ordinate: ndarray = (lambda: empty(0))(),
fit_region: tuple[int, int] = (0, 0),
intercept: float = 0.0,
kind: str = "",
q: float | None = None,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: ScalingResult
A fractal-dimension estimate with the log--log curve it was read from.
Returned by every estimator in this subpackage. A
:class:~tsdynamics.analysis._result.ScalingResult — the dimension is the
fitted slope of a log--log curve — so it inherits the canonical estimate /
abscissa / ordinate / fit_region schema, the result surface
(.meta / .summary() / .to_dict() / the .plot seam) and behaves
as the dimension number (float(result) and comparisons). Domain-named
@property aliases (:attr:dimension, :attr:x, :attr:y,
:attr:fit_slice) preserve the original field names.
| ATTRIBUTE | DESCRIPTION |
|---|---|
estimate |
The estimated dimension (the fitted slope). Aliased :attr:
TYPE:
|
stderr |
Standard error of the slope over the selected scaling region.
TYPE:
|
kind |
Which estimator produced it (
TYPE:
|
abscissa, ordinate |
The log--log curve the slope was fitted to (log-radius vs log-C for the
correlation sum; log-scale vs partition ordinate for the generalized
dimensions; mean-log-radius vs log-mass for fixed mass). Aliased
:attr:
TYPE:
|
fit_region |
Inclusive |
intercept |
Intercept of the fitted line.
TYPE:
|
q |
Rényi order, for the generalized dimensions (
TYPE:
|
x
property
¶
x: ndarray
The abscissa of the scaling curve (alias of :attr:abscissa; see :attr:kind).
y
property
¶
y: ndarray
The ordinate of the scaling curve (alias of :attr:ordinate; see :attr:kind).
fit_slice
property
¶
The selected scaling region (alias of :attr:fit_region).
local_slopes
property
¶
local_slopes: ndarray
Pointwise local slope of the log--log curve (the diagnostic plateau).
scaling_window
property
¶
The (x_lo, x_hi) abscissa span of the selected scaling region.
to_plot_spec
¶
Describe this dimension estimate as a backend-agnostic :class:PlotSpec.
Builds a SCALING_FIT spec — the log--log curve as a scatter layer, the
selected scaling region highlighted, and the fitted line drawn from
:attr:intercept and :attr:dimension — the same schema every scaling
estimator emits, so a single result.plot.scaling() renders it. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls a
plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/dimensions/_common.py
Delay embeddings¶
State-space reconstruction from a scalar (or multivariate) measurement (Takens, 1981): the time-delay map, plus the delay- and dimension-selection heuristics that parameterise it.
embed
¶
embed(
data: Any,
dimension: int | Sequence[int],
delay: int | Sequence[int],
*,
component: int | str | None = None,
) -> Embedding
Time-delay embedding of a scalar series (or a multivariate bundle).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source signal. A 1-D series (or a single selected
TYPE:
|
dimension
|
Embedding dimension :math:
TYPE:
|
delay
|
Delay :math:
TYPE:
|
component
|
Select a single channel from a multi-component |
| RETURNS | DESCRIPTION |
|---|---|
Embedding
|
The delay-coordinate matrix (behaves as an |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
The matrix is consumable directly by the point-set analyses — e.g.
correlation_dimension(embed(x, m, tau)) estimates :math:D_2 of the
reconstructed attractor. Rows are index-ordered with the original sampling,
so an index-based Theiler window still removes temporally-correlated pairs.
References
F. Takens, "Detecting strange attractors in turbulence", in Dynamical Systems and Turbulence, Lecture Notes in Mathematics 898, 366 (1981).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.embedding import embed
>>> x = np.arange(10.0)
>>> y = embed(x, dimension=3, delay=2)
>>> y.shape
(6, 3)
>>> y[0]
array([0., 2., 4.])
Source code in src/tsdynamics/analysis/embedding/embed.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
Delay selection¶
optimal_delay
¶
optimal_delay(
data: Any,
*,
method: str = "mi",
max_delay: int = 50,
bins: int | None = None,
component: int | str | None = None,
) -> CountResult
Recommend an embedding delay :math:\tau (in samples).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
method
|
TYPE:
|
max_delay
|
Largest lag considered.
TYPE:
|
bins
|
Histogram bins for the mutual-information estimate (
TYPE:
|
component
|
Component selector for a multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
CountResult
|
The recommended delay (behaves as an |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
If no first minimum / crossing is found within max_delay (e.g. a
slowly-decaying curve), the criterion's global fallback is used — the
location of the smallest mutual information, or max_delay for the
autocorrelation rules — so a usable delay is always returned.
The mutual-information criterion is the more widely recommended nonlinear measure (Fraser & Swinney 1986); the autocorrelation rules are the classic linear alternatives.
The first local minimum is taken at the onset of the dip: on a
flat-bottomed (quantised) mutual-information valley the recommended
:math:\tau is the first lag of the plateau, not its far edge — so the
delay is not over-estimated on coarsely sampled curves.
References
A. M. Fraser and H. L. Swinney, "Independent coordinates for strange attractors from mutual information", Phys. Rev. A 33, 1134 (1986).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.embedding import optimal_delay
>>> t = np.linspace(0.0, 100.0, 4000)
>>> x = np.sin(2.0 * np.pi * 0.05 * t)
>>> int(optimal_delay(x, method="mi", max_delay=60)) >= 1
True
Source code in src/tsdynamics/analysis/embedding/delay.py
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 | |
mutual_information
¶
mutual_information(
data: Any,
*,
max_delay: int = 50,
bins: int | None = None,
base: float = e,
component: int | str | None = None,
) -> MutualInformation
Time-delayed mutual information :math:I(\tau) up to max_delay.
The histogram estimator of
.. math::
I(\tau) = \sum_{a,b} p_{ab}(\tau)\,
\log\frac{p_{ab}(\tau)}{p_a\, p_b},
where :math:p_{ab} is the joint distribution of :math:(x_i, x_{i+\tau})
over an equal-width 2-D histogram and :math:p_a, p_b its marginals.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
max_delay
|
Largest lag returned. Clamped to
TYPE:
|
bins
|
Number of histogram bins per axis. Default: a sample-size-dependent
rule,
TYPE:
|
base
|
Logarithm base —
TYPE:
|
component
|
Component selector for a multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
MutualInformation
|
Behaves as an |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
A. M. Fraser and H. L. Swinney, "Independent coordinates for strange attractors from mutual information", Phys. Rev. A 33, 1134 (1986).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.embedding import mutual_information
>>> t = np.linspace(0.0, 100.0, 2000)
>>> x = np.sin(2.0 * np.pi * 0.1 * t)
>>> mi = mutual_information(x, max_delay=40)
>>> int(mi.optimal_lag) >= 1
True
Source code in src/tsdynamics/analysis/embedding/delay.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 | |
autocorrelation
¶
autocorrelation(
data: Any,
*,
max_delay: int = 50,
component: int | str | None = None,
) -> ndarray
Normalised autocorrelation function up to max_delay.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
max_delay
|
Largest lag returned. Clamped to
TYPE:
|
component
|
Component selector for a multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(max_delay + 1))
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
Computed via FFT (Wiener--Khinchin) on the mean-subtracted series. This is the standard biased autocorrelation estimator — each lag is normalised by the full zero-lag variance (not by the shrinking overlap count at that lag) — which is positive-definite and the conventional choice for delay selection.
Source code in src/tsdynamics/analysis/embedding/delay.py
Dimension selection¶
embedding_dimension
¶
embedding_dimension(
data: Any,
*,
method: str = "cao",
delay: int = 1,
max_dim: int = 10,
component: int | str | None = None,
**kwargs: Any,
) -> EmbeddingDimension
Estimate the minimum embedding dimension by the chosen method.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
method
|
TYPE:
|
delay
|
Embedding delay in samples.
TYPE:
|
max_dim
|
Largest dimension evaluated.
TYPE:
|
component
|
Component selector for a multi-component input. |
**kwargs
|
Forwarded to the selected estimator (
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
EmbeddingDimension
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
See Also
cao_dimension : Cao's averaged false-neighbour estimator. false_nearest_neighbors : Kennel's false-nearest-neighbour estimator.
Source code in src/tsdynamics/analysis/embedding/dimension.py
cao_dimension
¶
cao_dimension(
data: Any,
*,
delay: int = 1,
max_dim: int = 10,
threshold: float = 0.9,
theiler: int = 0,
component: int | str | None = None,
) -> EmbeddingDimension
Cao's averaged-false-neighbour minimum embedding dimension.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
delay
|
Embedding delay :math:
TYPE:
|
max_dim
|
Largest dimension :math:
TYPE:
|
threshold
|
Saturation threshold: the estimate is the smallest :math:
TYPE:
|
theiler
|
Exclude temporally-close neighbours with :math:
TYPE:
|
component
|
Component selector for a multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
EmbeddingDimension
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
The d = 1 point of the returned :math:E_1 / :math:E_2 curves
(afn_e1[0] / afn_e2[0]) is diagnostically unreliable. At
:math:d = 1 the base distance :math:R_d is the 1-D nearest-neighbour gap
:math:|x_i - x_n|; on a densely sampled series, coincident-but-distinct
samples make :math:R_d arbitrarily small, so the per-point ratio
:math:a(i, 1) = R_{d+1}/R_d can blow up and inflate the mean by orders of
magnitude. This corrupts only the first point of the diagnostic curve and
does not affect the recommended dimension :math:m, which is read off the
saturation plateau past :math:d = 1 (use a Theiler window of a few
autocorrelation times to mitigate the artefact).
References
L. Cao, "Practical method for determining the minimum embedding dimension of a scalar time series", Physica D 110, 43 (1997).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.embedding import cao_dimension
>>> t = np.linspace(0.0, 200.0, 4000)
>>> x = np.sin(t) + 0.5 * np.sin(2.0 * t)
>>> int(cao_dimension(x, delay=10, max_dim=8)) >= 2
True
Source code in src/tsdynamics/analysis/embedding/dimension.py
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 | |
false_nearest_neighbors
¶
false_nearest_neighbors(
data: Any,
*,
delay: int = 1,
max_dim: int = 10,
rtol: float = 15.0,
atol: float = 2.0,
threshold: float = 0.01,
theiler: int = 0,
component: int | str | None = None,
) -> EmbeddingDimension
Kennel's false-nearest-neighbour minimum embedding dimension.
A neighbour found in dimension :math:d is false when extending to
:math:d+1 either stretches the pair by more than rtol relative to their
:math:d-dimensional distance, or pushes them apart by more than atol
relative to the attractor size :math:R_A (the series' standard deviation) —
the second test catching neighbours whose :math:d-dimensional distance is
essentially zero.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The scalar series (or a selected
TYPE:
|
delay
|
Embedding delay :math:
TYPE:
|
max_dim
|
Largest dimension evaluated.
TYPE:
|
rtol
|
First-criterion tolerance :math:
TYPE:
|
atol
|
Second-criterion tolerance :math:
TYPE:
|
threshold
|
The estimate is the smallest :math:
TYPE:
|
theiler
|
Exclude temporally-close neighbours with :math:
TYPE:
|
component
|
Component selector for a multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
EmbeddingDimension
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
M. B. Kennel, R. Brown and H. D. I. Abarbanel, "Determining embedding dimension for phase-space reconstruction using a geometrical construction", Phys. Rev. A 45, 3403 (1992).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.embedding import false_nearest_neighbors
>>> t = np.linspace(0.0, 200.0, 4000)
>>> x = np.sin(t) + 0.5 * np.sin(2.0 * t)
>>> int(false_nearest_neighbors(x, delay=10, max_dim=8)) >= 2
True
Source code in src/tsdynamics/analysis/embedding/dimension.py
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 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 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 | |
EmbeddingDimension
dataclass
¶
EmbeddingDimension(
dimension: int = 0,
dims: ndarray = (lambda: empty(0))(),
method: str = "",
delay: int = 0,
afn_e1: ndarray | None = None,
afn_e2: ndarray | None = None,
fnn_fraction: ndarray | None = None,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A minimum-embedding-dimension estimate with the curve it was read from.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam. It also
behaves as the dimension integer (int(result) drops it straight into
:func:~tsdynamics.analysis.embedding.embed.embed), while carrying the
per-dimension diagnostic so the saturation/decay can be inspected.
| ATTRIBUTE | DESCRIPTION |
|---|---|
dimension |
The recommended minimum embedding dimension :math:
TYPE:
|
dims |
The dimensions :math:
TYPE:
|
method |
TYPE:
|
delay |
The delay (in samples) used to build the reconstructions.
TYPE:
|
afn_e1, afn_e2 |
Cao's :math:
TYPE:
|
fnn_fraction |
Kennel's false-nearest-neighbour fraction per dimension (decays to 0).
TYPE:
|
to_plot_spec
¶
Describe the embedding-dimension diagnostic as a :class:PlotSpec.
Builds a DIAGNOSTIC_CURVE of the per-dimension diagnostic against the
embedding dimension :math:d, with the selected dimension :math:m
annotated as a vertical reference line:
- for Cao's method, both :math:
E_1(d)(saturates to 1 at the right dimension) and :math:E_2(d)(stays near 1 for stochastic data) are drawn, with a reference line at the saturation level :math:1; - for Kennel's FNN, the false-nearest-neighbour fraction (decays to 0) is drawn.
The selected :math:m is read straight off :attr:dimension, so the
vertical line marks where the curve has saturated/decayed. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls a
plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
| RAISES | DESCRIPTION |
|---|---|
VisualizationNotInstalled
|
If neither the Cao curves nor the FNN fraction is present (nothing to draw) — the generic-fallback contract. |
Source code in src/tsdynamics/analysis/embedding/dimension.py
Recurrence & RQA¶
Recurrence plots (Eckmann, Kamphorst & Ruelle, 1987) record when a trajectory revisits its own past; recurrence quantification analysis (Marwan et al., 2007) reduces that structure to scalar measures of determinism and laminarity, run globally or in a sliding window.
recurrence_matrix
¶
recurrence_matrix(
data: Any,
*,
threshold: float | None = None,
recurrence_rate: float | None = None,
metric: str | float = "euclidean",
theiler: int = 0,
) -> RecurrenceMatrix
Build a recurrence matrix from a trajectory or point set.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The state points (a :class:
TYPE:
|
threshold
|
Fixed distance threshold :math:
TYPE:
|
recurrence_rate
|
Target matrix density in
TYPE:
|
metric
|
Distance metric ( |
theiler
|
Exclude the near-diagonal band :math:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
RecurrenceMatrix
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If neither or both of |
References
J.-P. Eckmann, S. O. Kamphorst and D. Ruelle, "Recurrence plots of dynamical systems", Europhys. Lett. 4, 973 (1987).
Source code in src/tsdynamics/analysis/recurrence/matrix.py
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | |
RecurrenceMatrix
dataclass
¶
RecurrenceMatrix(
matrix: Any = None,
epsilon: float = 0.0,
metric: str | float = "euclidean",
theiler_window: int = 0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A binary recurrence matrix with the parameters it was built from.
The matrix is symmetric, sparse and excludes the line of identity (plus the
Theiler band when theiler_window > 0). Pass it straight to
:func:~tsdynamics.analysis.rqa for quantification, or read
:attr:recurrence_rate / densify with :meth:toarray for inspection.
| ATTRIBUTE | DESCRIPTION |
|---|---|
matrix |
The :math:
TYPE:
|
epsilon |
The distance threshold actually used.
TYPE:
|
metric |
The metric the threshold is measured in. |
theiler_window |
Excluded near-diagonal band :math:
TYPE:
|
to_plot_spec
¶
Describe this recurrence matrix as a backend-agnostic :class:PlotSpec.
Builds a RECURRENCE_PLOT as a sparse SCATTER of the recurrent
pairs: the stored COO row / column indices become the (i, j) point
cloud, one marker per recurrence, on a square (aspect="equal") canvas
with both axes labelled by the state index. The matrix is never
densified — the coordinate arrays have length equal to the number of
stored recurrences (nnz), so a recurrence plot of a long, sparse
series stays :math:O(\#\text{recurrences}) in memory rather than the
:math:O(N^2) a dense image would cost. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls a
plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/recurrence/matrix.py
rqa
¶
rqa(
data: Any,
*,
threshold: float | None = None,
recurrence_rate: float | None = None,
metric: str | float = "euclidean",
theiler: int = 0,
min_diagonal: int = 2,
min_vertical: int = 2,
) -> RQAResult
Recurrence quantification of a trajectory, series, or recurrence matrix.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
A prebuilt :class:
TYPE:
|
threshold
|
Threshold or target recurrence rate when
TYPE:
|
recurrence_rate
|
Threshold or target recurrence rate when
TYPE:
|
metric
|
Distance metric (ignored when |
theiler
|
Excluded near-diagonal band (ignored when
TYPE:
|
min_diagonal
|
Shortest diagonal line counted toward
TYPE:
|
min_vertical
|
Shortest vertical line counted toward
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
RQAResult
|
The recurrence-quantification measures (RR, DET, LAM, L, L_max, DIV, ENTR, TT, V_max) plus the raw diagonal / vertical line-length histograms. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Notes
L_max and V_max are the longest diagonal / vertical lines over the
full line-length histograms (no min_* filter), matching Marwan et al.
(2007); DIV = 1 / L_max. The min_diagonal / min_vertical cut
applies only to the ratio and mean measures (DET / LAM / L / TT / ENTR).
References
N. Marwan, M. C. Romano, M. Thiel and J. Kurths, "Recurrence plots for the analysis of complex systems", Phys. Rep. 438, 237 (2007).
Examples:
>>> import numpy as np
>>> import tsdynamics as ts
>>> t = np.linspace(0.0, 100.0, 1000)
>>> emb = ts.embed(np.sin(t), dimension=2, delay=5)
>>> res = ts.rqa(emb, recurrence_rate=0.05)
>>> 0.0 <= res.determinism <= 1.0
True
Source code in src/tsdynamics/analysis/recurrence/rqa.py
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | |
RQAResult
dataclass
¶
RQAResult(
recurrence_rate: float,
determinism: float,
laminarity: float,
avg_diagonal_length: float,
max_diagonal_length: int,
divergence: float,
diagonal_entropy: float,
trapping_time: float,
max_vertical_length: int,
size: int,
epsilon: float,
theiler_window: int,
min_diagonal: int,
min_vertical: int,
diagonal_lengths: ndarray,
vertical_lengths: ndarray,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Recurrence-quantification measures of one recurrence matrix.
| ATTRIBUTE | DESCRIPTION |
|---|---|
recurrence_rate |
TYPE:
|
determinism |
TYPE:
|
laminarity |
TYPE:
|
avg_diagonal_length |
TYPE:
|
max_diagonal_length |
TYPE:
|
divergence |
TYPE:
|
diagonal_entropy |
TYPE:
|
trapping_time |
TYPE:
|
max_vertical_length |
TYPE:
|
size |
Number of states
TYPE:
|
epsilon |
Threshold the matrix was built with.
TYPE:
|
theiler_window |
Excluded near-diagonal band.
TYPE:
|
min_diagonal, min_vertical |
Minimum line lengths counted as diagonal / vertical lines.
TYPE:
|
diagonal_lengths, vertical_lengths |
Raw line-length histograms (every run, before the
TYPE:
|
to_plot_spec
¶
Describe the scalar RQA measures as a :class:PlotSpec bar readout.
Builds a CATEGORICAL_BAR whose bars are the headline structure
quantifiers — RR (recurrence rate), DET (determinism), LAM
(laminarity) and ENTR (diagonal-line entropy) — one bar per measure,
the category axis carrying the measure labels. This is the at-a-glance
readout of "how deterministic / laminar is this trajectory"; the
unbounded measures (L_max / V_max / DIV) stay in
:meth:summary rather than crushing the bar scale. No line-length
histogram is walked — the values are the already-computed scalar fields.
The :mod:tsdynamics.viz.spec import is lazy, so building a spec never
pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/recurrence/rqa.py
windowed_rqa
¶
windowed_rqa(
data: Any,
*,
window: int,
step: int | None = None,
threshold: float | None = None,
recurrence_rate: float | None = None,
metric: str | float = "euclidean",
theiler: int = 0,
min_diagonal: int = 2,
min_vertical: int = 2,
) -> WindowedRQA
Run :func:~tsdynamics.analysis.rqa in a sliding window.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The state points (or a 1-D series).
TYPE:
|
window
|
Window length in samples (
TYPE:
|
step
|
Stride between windows (default:
TYPE:
|
threshold
|
Exactly one; passed to each window's matrix (see
:func:
TYPE:
|
recurrence_rate
|
Exactly one; passed to each window's matrix (see
:func:
TYPE:
|
metric
|
Distance metric. |
theiler
|
Excluded near-diagonal band, applied within each window.
TYPE:
|
min_diagonal
|
Minimum line lengths (see :func:
TYPE:
|
min_vertical
|
Minimum line lengths (see :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
WindowedRQA
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/tsdynamics/analysis/recurrence/windowed.py
WindowedRQA
dataclass
¶
WindowedRQA(
centers: ndarray = (lambda: empty(0))(),
results: tuple[RQAResult, ...] = (),
window: int = 0,
step: int = 0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
RQA measures over a sliding window.
Each scalar measure of :class:~tsdynamics.analysis.RQAResult is available as
a per-window array of the same length as :attr:centers (the window-centre
sample indices), e.g. windowed.determinism or
windowed.measure("laminarity").
| ATTRIBUTE | DESCRIPTION |
|---|---|
centers |
Window-centre positions in sample units (
TYPE:
|
results |
The per-window results, in order. |
window |
Window length in samples.
TYPE:
|
step |
Stride between consecutive windows in samples.
TYPE:
|
measure
¶
Return one RQA measure as an array over windows.
| PARAMETER | DESCRIPTION |
|---|---|
name
|
Any scalar attribute of :class:
TYPE:
|
Source code in src/tsdynamics/analysis/recurrence/windowed.py
to_plot_spec
¶
Describe the windowed RQA as a :class:PlotSpec.
Builds a DIAGNOSTIC_CURVE carrying a LINE of the determinism
measure against the window-centre index — the canonical sliding-RQA view
for spotting dynamical regime transitions (a drop in determinism flags a
shift away from deterministic/periodic behaviour). Read any other measure
off :meth:measure. The :mod:tsdynamics.viz.spec import is lazy.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/recurrence/windowed.py
Surrogates & nonlinearity tests¶
The surrogate-data method (Theiler et al., 1992; Schreiber & Schmitz, 1996) tests a series for nonlinear structure against an ensemble of surrogates that reproduce its linear properties (amplitude distribution and/or power spectrum) but are otherwise random.
surrogate_test
¶
surrogate_test(
data: Any,
statistic: str | Callable[..., float] = "time_reversal",
method: str = "iaaft",
n: int = 39,
*,
tail: str = "auto",
alpha: float = 0.05,
seed: int | None = None,
component: int | str | None = None,
statistic_kwargs: dict[str, Any] | None = None,
**surrogate_kwargs: Any,
) -> SurrogateTest
Test a series for nonlinearity against linear surrogates.
Evaluates statistic on data and on n surrogates drawn by
method, then reports the rank p-value and significance of the data
statistic within the surrogate ensemble. The default — time-reversal asymmetry
against IAAFT surrogates — rejects the linear-Gaussian null for a dissipative
chaotic flow such as Lorenz.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The series under test (a component of a multi-component input is selected
with
TYPE:
|
statistic
|
|
method
|
Surrogate method —
TYPE:
|
n
|
Number of surrogates. Theiler's :math:
TYPE:
|
tail
|
Rejection tail.
TYPE:
|
alpha
|
Significance level for the
TYPE:
|
seed
|
Seed for the surrogate ensemble (makes the whole test reproducible).
TYPE:
|
component
|
Component to select from multi-component input. |
statistic_kwargs
|
Extra keyword arguments forwarded to the statistic.
TYPE:
|
**surrogate_kwargs
|
Extra keyword arguments forwarded to the generator (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
SurrogateTest
|
The test outcome. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
TypeError
|
If a non-integer |
References
.. [1] J. Theiler, S. Eubank, A. Longtin, B. Galdrikian, and J. D. Farmer, "Testing for nonlinearity in time series: the method of surrogate data," Physica D 58, 77-94 (1992).
Examples:
>>> import numpy as np
>>> from tsdynamics.analysis.surrogate import surrogate_test
>>> rng = np.random.default_rng(0)
>>> noise = rng.standard_normal(2000) # a linear-Gaussian null
>>> res = surrogate_test(noise, "time_reversal", n=39, seed=0)
>>> bool(res.rejected)
False
Source code in src/tsdynamics/analysis/surrogate/hypothesis.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
SurrogateTest
dataclass
¶
SurrogateTest(
data_statistic: float = 0.0,
surrogate_statistics: ndarray = (lambda: empty(0))(),
p_value: float = 1.0,
z_score: float = 0.0,
rejected: bool = False,
statistic: str = "",
method: str = "",
n_surrogates: int = 0,
tail: str = "two",
alpha: float = 0.05,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Outcome of a surrogate-data nonlinearity test.
An :class:~tsdynamics.analysis._result.AnalysisResult, so it carries
.meta / .summary() / .to_dict() / the .plot seam alongside the
test outcome.
| ATTRIBUTE | DESCRIPTION |
|---|---|
data_statistic |
The discriminating statistic evaluated on the data.
TYPE:
|
surrogate_statistics |
The same statistic on each surrogate (shape
TYPE:
|
p_value |
The rank-based surrogate p-value for the chosen
TYPE:
|
z_score |
Significance of the data statistic in surrogate standard deviations (the classic "number of sigmas"); positive when the data lies above the mean.
TYPE:
|
rejected |
Whether the linear null is rejected at
TYPE:
|
statistic |
Name of the statistic (
TYPE:
|
method |
Surrogate method used.
TYPE:
|
n_surrogates |
Number of surrogates drawn.
TYPE:
|
tail |
The rejection tail (
TYPE:
|
alpha |
The significance level the rejection decision used.
TYPE:
|
to_plot_spec
¶
Describe this surrogate test as a backend-agnostic :class:PlotSpec.
Builds a HISTOGRAM_NULL spec — the surrogate-statistic ensemble as a
histogram (the null distribution) with the data statistic marked as a
vertical reference line and the alpha-quantile rejection tail(s)
shaded (a "span" annotation per tail, picked by :attr:tail) — so
the rejection reads off as the data line landing inside a shaded tail.
The :mod:tsdynamics.viz.spec import is lazy, so building a spec never
pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/surrogate/hypothesis.py
Generators¶
surrogates
¶
surrogates(
data: Any,
method: str = "iaaft",
n: int = 1,
*,
seed: int | None = None,
component: int | str | None = None,
**kwargs: Any,
) -> SurrogateEnsemble
Generate surrogate series by name — the dispatcher every test goes through.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source series.
TYPE:
|
method
|
One of
TYPE:
|
n
|
Number of surrogates to draw.
TYPE:
|
seed
|
Seed for reproducibility.
TYPE:
|
component
|
Component to select from multi-component input. |
**kwargs
|
Forwarded to the generator (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
SurrogateEnsemble
|
The surrogate ensemble; behaves as the |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
Source code in src/tsdynamics/analysis/surrogate/generators.py
random_shuffle
¶
random_shuffle(
data: Any,
n: int = 1,
*,
seed: int | None = None,
component: int | str | None = None,
) -> ndarray
Random-permutation surrogates (the constrained-realisation i.i.d. null).
Each surrogate is an independent random permutation of the samples, so it keeps the amplitude distribution exactly and destroys every temporal correlation — the appropriate null for "are successive samples independent?".
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source series (see :func:
TYPE:
|
n
|
Number of surrogates to draw.
TYPE:
|
seed
|
Seed for reproducibility.
TYPE:
|
component
|
Component to select from multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n, N))
|
The surrogate ensemble. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
.. [1] J. Theiler, S. Eubank, A. Longtin, B. Galdrikian, and J. D. Farmer, "Testing for nonlinearity in time series: the method of surrogate data," Physica D 58, 77-94 (1992).
Source code in src/tsdynamics/analysis/surrogate/generators.py
fourier_surrogate
¶
fourier_surrogate(
data: Any,
n: int = 1,
*,
seed: int | None = None,
component: int | str | None = None,
) -> ndarray
Phase-randomised (Fourier transform) surrogates of a linear Gaussian null.
Keeps the magnitude spectrum — and therefore the power spectrum and the linear
autocorrelation — of data exactly while randomising the Fourier phases
(Theiler et al., 1992). The surrogate amplitude distribution drifts toward
Gaussian; use :func:aaft_surrogate / :func:iaaft_surrogate when the
distribution must be preserved too.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source series.
TYPE:
|
n
|
Number of surrogates to draw.
TYPE:
|
seed
|
Seed for reproducibility.
TYPE:
|
component
|
Component to select from multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n, N))
|
The surrogate ensemble. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
.. [1] J. Theiler, S. Eubank, A. Longtin, B. Galdrikian, and J. D. Farmer, "Testing for nonlinearity in time series: the method of surrogate data," Physica D 58, 77-94 (1992).
Source code in src/tsdynamics/analysis/surrogate/generators.py
aaft_surrogate
¶
aaft_surrogate(
data: Any,
n: int = 1,
*,
seed: int | None = None,
component: int | str | None = None,
) -> ndarray
Amplitude-adjusted Fourier-transform surrogates (Theiler et al., 1992).
Tests the null of a static monotonic nonlinearity acting on a linear Gaussian
process: the data is mapped to Gaussian by rank, phase-randomised, then mapped
back through the inverse rank transform. The result reproduces the amplitude
distribution exactly and the power spectrum approximately (the rank remap
distorts the spectrum slightly — :func:iaaft_surrogate removes that bias).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source series.
TYPE:
|
n
|
Number of surrogates to draw.
TYPE:
|
seed
|
Seed for reproducibility.
TYPE:
|
component
|
Component to select from multi-component input. |
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n, N))
|
The surrogate ensemble. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
.. [1] J. Theiler, S. Eubank, A. Longtin, B. Galdrikian, and J. D. Farmer, "Testing for nonlinearity in time series: the method of surrogate data," Physica D 58, 77-94 (1992).
Source code in src/tsdynamics/analysis/surrogate/generators.py
iaaft_surrogate
¶
iaaft_surrogate(
data: Any,
n: int = 1,
*,
seed: int | None = None,
component: int | str | None = None,
max_iter: int = 1000,
) -> ndarray
Refine AAFT surrogates iteratively (IAAFT; Schreiber & Schmitz, 1996).
Alternates two projections until the sample ordering stops changing: a
spectral step that restores the exact magnitude spectrum of data (keeping
the current phases), and an amplitude step that restores data's exact
sorted values by rank. Because it ends on the amplitude step, the surrogate's
amplitude distribution is exact and its power spectrum matches to high accuracy
— the standard improvement over plain :func:aaft_surrogate.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The source series.
TYPE:
|
n
|
Number of surrogates to draw.
TYPE:
|
seed
|
Seed for reproducibility.
TYPE:
|
component
|
Component to select from multi-component input. |
max_iter
|
Cap on refinement iterations per surrogate (convergence is detected when the rank order repeats; the cap only bites on hard cases).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n, N))
|
The surrogate ensemble. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
.. [1] T. Schreiber and A. Schmitz, "Improved surrogate data for nonlinearity tests," Physical Review Letters 77, 635-638 (1996).
Source code in src/tsdynamics/analysis/surrogate/generators.py
Statistics¶
time_reversal_asymmetry
¶
Time-reversal asymmetry statistic of a scalar series.
Computes the dimensionless third-moment ratio of the lagged increments,
.. math::
T_\text{rev} = \frac{\langle (x_{t} - x_{t-\ell})^3 \rangle}
{\langle (x_{t} - x_{t-\ell})^2 \rangle^{3/2}},
which changes sign under time reversal and is therefore identically zero (in
expectation) for any time-reversible process — including a linear Gaussian
process and any static monotonic transform of one, the nulls the Fourier-family
surrogates realise. A dissipative deterministic flow breaks that symmetry, so
a large :math:|T_\text{rev}| relative to the surrogates flags nonlinearity.
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The 1-D series.
TYPE:
|
delay
|
The increment lag :math:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
The asymmetry ratio ( |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
.. [1] T. Schreiber and A. Schmitz, "Improved surrogate data for nonlinearity tests," Physical Review Letters 77, 635-638 (1996). .. [2] C. Diks, J. C. van Houwelingen, F. Takens, and J. DeGoede, "Reversibility as a criterion for discriminating time series," Physics Letters A 201, 221-228 (1995).
Source code in src/tsdynamics/analysis/surrogate/statistics.py
nonlinear_prediction_error
¶
nonlinear_prediction_error(
data: ndarray,
dimension: int = 3,
delay: int = 1,
*,
horizon: int = 1,
n_neighbors: int = 4,
theiler: int = 1,
) -> float
Out-of-sample error of a locally-constant phase-space predictor.
Reconstructs the series in dimension delay coordinates (delay delay)
and, for each point, predicts the value horizon steps ahead as the mean of
the futures of its n_neighbors nearest phase-space neighbours, excluding
temporal neighbours within a Theiler window. The returned error is the
root-mean-square prediction residual normalised by the standard deviation of the
series, so a perfectly unpredictable series scores :math:\approx 1 and a
deterministic one scores well below it (Sugihara & May, 1990; Kantz &
Schreiber, 2004).
Because determinism is exactly what the linear surrogates lack, a small error
relative to the surrogate ensemble is evidence of nonlinear determinism (use a
one-sided tail="less" test).
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The 1-D series.
TYPE:
|
dimension
|
Embedding dimension,
TYPE:
|
delay
|
Embedding delay in samples,
TYPE:
|
horizon
|
Prediction horizon in samples,
TYPE:
|
n_neighbors
|
Number of nearest neighbours averaged per prediction.
TYPE:
|
theiler
|
Half-width of the temporal-exclusion (Theiler) window; neighbours with
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
The normalised RMS prediction error. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the parameters are out of range or the series is too short to embed and find the requested neighbours. |
References
.. [1] G. Sugihara and R. M. May, "Nonlinear forecasting as a way of distinguishing chaos from measurement error in time series," Nature 344, 734-741 (1990). .. [2] H. Kantz and T. Schreiber, Nonlinear Time Series Analysis, 2nd ed., Cambridge University Press (2004).
Source code in src/tsdynamics/analysis/surrogate/statistics.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | |
Attractors & basins¶
The global stability picture of a multistable system. Attractors are located by following trajectories through a cell tessellation until they recurrently revisit cells (Datseris & Wagemakers, 2022); the basin of each is the set of initial conditions reaching it. Basin stability (Menck et al., 2013) is an attractor's share of a sampled region; basin entropy (Daza et al., 2016) and the uncertainty exponent (Grebogi et al., 1983) quantify how fractal the boundaries are; continuation tracks attractors and their basins across a parameter.
find_attractors
¶
find_attractors(
system: Any,
region: Box | Ball | Grid,
*,
resolution: int | tuple[int, ...] = 100,
n_seeds: int = 1000,
seed: int | None = 0,
dt: float = 1.0,
max_steps: int = 10000,
merge_tol: float | None = None,
**fsm: Any,
) -> AttractorSet
Find the attractors a system has within a region, via recurrences.
Tessellate region into cells, draw n_seeds random initial conditions
from it, and follow each until it settles into a recurrent cell set (a new
attractor) or inherits one already found (Datseris & Wagemakers, 2022).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A discrete map or continuous flow. Delay and stochastic systems are not supported (their state is not a finite-dimensional point).
TYPE:
|
region
|
Where to sample initial conditions and the box the recurrence cells cover.
TYPE:
|
resolution
|
Recurrence cells per axis when
TYPE:
|
n_seeds
|
Number of random initial conditions to classify.
TYPE:
|
seed
|
Seed for the initial-condition sampler (reproducible).
TYPE:
|
dt
|
Integration step between cell checks for a flow (ignored for a map).
TYPE:
|
max_steps
|
Per-seed step cap before declaring divergence.
TYPE:
|
merge_tol
|
Merge attractors whose centroids lie within this distance (a split-set
cleanup).
TYPE:
|
**fsm
|
Finite-state-machine thresholds forwarded to :class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
AttractorSet
|
The located attractors plus how many seeds diverged. |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
Notes
The seed march is sequential by design, not a parallelism oversight: each
seed is followed cell-by-cell, and the persistent cell labels (_att_cells /
_bas_cells) accumulated by earlier seeds let later seeds settle cheaply by
reaching an already-labelled cell. That shared, order-dependent labelling state
is what makes the sweep amortise — and is exactly why the march cannot be
parallelised without changing the result or the determinism.
On a supported engine run (an ODE flow or a map whose _step lowers, on the
interp / jit backend) the whole per-IC march now runs in one
sequential Rust kernel call (stream perf/basin-march) — stepping,
cell-binning and the shared-label early-out all in Rust, with no per-dt
Python→FFI round-trip — so it is fast without parallelism. The per-cell-check
numerics are byte-for-byte the released system.step(), so the result is
bit-identical to the pure-Python loop, which stays the fallback (and the oracle)
for reference, a non-lowering _step, and DDE/SDE systems.
Why thread-parallelism is a net loss here, and why the kernel is therefore
serial (measured, so the next reader does not re-derive it). The shared
early-out is the dominant work-saver: on the two-well Duffing 60×60 grid a
serial march takes ~42k engine steps, whereas marching every seed independently
to max_steps (the only way to lift the serial label dependency) takes
~1.4M — a ~34× work inflation that 16 cores cannot recover (the independent
full-march alone clocked ~5× slower than the whole serial run). A
"speculative march in parallel, fold in seed order" scheme would reproduce the
labels bit-for-bit (each seed's state stream is a pure function of its IC) but
pays exactly that 34× over-march, so it is abandoned. Dense-block FFI batching
is not an option either: the FSM checks the cell after every per-dt
step() restart, and an adaptive dense-output block over several dt does
not reproduce those per-dt restart states bit-for-bit (it drifts at ~5e-7),
so it would change the cell sequence and the labels. The march stays serial
because that is the algorithm; the Rust kernel makes that serial march cheap.
References
G. Datseris and A. Wagemakers, "Effortless estimation of basins of attraction", Chaos 32, 023104 (2022).
Source code in src/tsdynamics/analysis/basins/attractors.py
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 827 | |
AttractorSet
dataclass
¶
AttractorSet(
attractors: dict[int, Attractor] = dict(),
diverged: int = 0,
seeds: int = 0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
The attractors found in a region, keyed by integer id.
| ATTRIBUTE | DESCRIPTION |
|---|---|
attractors |
Located attractors, id → :class: |
diverged |
How many seeds left the region / never settled.
TYPE:
|
seeds |
How many seeds were classified in total.
TYPE:
|
match
¶
Return the id of the attractor closest to point (or None if empty).
Uses :func:tsdynamics.data.set_distance; point may be a single state
or a point cloud.
Source code in src/tsdynamics/analysis/basins/attractors.py
to_plot_spec
¶
Describe the located attractors as a backend-agnostic :class:PlotSpec.
Builds a PHASE_PORTRAIT_2D of every attractor's point cloud as one
SCATTER layer (the first two state coordinates). Each point carries
a "cat" channel — the attractor id's swatch index in the shared
categorical palette (tab20) — so the same id is drawn the same colour
here and on the basin image (:meth:BasinsResult.to_plot_spec). The
palette name and the fixed diverged colour are recorded in meta so a
renderer can reproduce the mapping; the colorbar is marked
:attr:~tsdynamics.viz.spec.Colorbar.discrete.
Each id's representative also seeds a category label, so the colour key
reads as attractor 1, attractor 2, …. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls
a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
| RAISES | DESCRIPTION |
|---|---|
VisualizationNotInstalled
|
If the set holds no attractor with a ≥ 2-D point cloud to scatter. |
Source code in src/tsdynamics/analysis/basins/attractors.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 | |
Attractor
dataclass
¶
Attractor(
id: int = 0,
points: ndarray = (lambda: empty((0, 0)))(),
cells: int = 0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
One located attractor: a point cloud of states sampled on it.
| ATTRIBUTE | DESCRIPTION |
|---|---|
id |
Integer label (
TYPE:
|
points |
States sampled while the trajectory was on the attractor. A fixed point collapses to one repeated point; a cycle/chaotic set spreads out.
TYPE:
|
cells |
Number of distinct grid cells the attractor occupies (a coarse size).
TYPE:
|
basins_of_attraction
¶
basins_of_attraction(
system: Any,
region: Grid,
*,
recurrence: Box | Grid | None = None,
recurrence_resolution: int | tuple[int, ...] = 100,
seed: int | None = 0,
dt: float = 1.0,
max_steps: int = 10000,
merge_tol: float | None = None,
**fsm: Any,
) -> BasinsResult
Classify every point of a grid region by the attractor it converges to.
Each lattice point is followed until it settles into a recurrent cell set (Datseris & Wagemakers, 2022); by default the region doubles as the recurrence tessellation, so labels accumulate and most points settle cheaply by reaching an already-labelled cell.
For a higher-dimensional flow whose basins are viewed on a slice (e.g. a
position grid of a system that also carries velocities), pass a full-dimension
recurrence box covering the whole trajectory range and let region be the
thin slice of initial conditions (free axes pinned with counts == 1).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A discrete map or continuous flow.
TYPE:
|
region
|
The lattice of initial conditions (full state dimension; a slice pins free
axes with
TYPE:
|
recurrence
|
Full-dimension region whose tessellation recurrences are detected on.
Defaults to |
recurrence_resolution
|
Recurrence cells per axis when
TYPE:
|
seed
|
Accepted for signature uniformity with :func:
TYPE:
|
dt
|
Integration step between cell checks for a flow (ignored for a map).
TYPE:
|
max_steps
|
Per-point step cap before declaring divergence.
TYPE:
|
merge_tol
|
Merge attractors whose centroids lie within this distance.
TYPE:
|
**fsm
|
Finite-state-machine thresholds forwarded to
:class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
BasinsResult
|
The labelled basin image and the attractors it refers to. |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
References
G. Datseris and A. Wagemakers, "Effortless estimation of basins of attraction", Chaos 32, 023104 (2022).
Source code in src/tsdynamics/analysis/basins/basins.py
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 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 | |
BasinsResult
dataclass
¶
BasinsResult(
labels: ndarray = (lambda: empty(0, dtype=int))(),
grid: Grid | None = None,
attractors: AttractorSet = AttractorSet(),
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A basin diagram: every grid cell labelled by the attractor it reaches.
| ATTRIBUTE | DESCRIPTION |
|---|---|
labels |
Attractor id (
TYPE:
|
grid |
The lattice the labels are laid out on.
TYPE:
|
attractors |
The attractors the labels refer to.
TYPE:
|
fractions
property
¶
Fraction of grid cells in each attractor's basin (diverged cells excluded).
Keyed by attractor id (>= 1); the diverged share is reported separately
by :attr:diverged_fraction (so this mirrors
:attr:BasinFractions.fractions).
diverged_fraction
property
¶
diverged_fraction: float
Fraction of cells that diverged / never settled.
to_plot_spec
¶
Describe this basin diagram as a backend-agnostic :class:PlotSpec.
Builds a BASINS_IMAGE spec — the integer label field as an image on
an "equal" canvas, the grid axes giving the extent, plus a marker
layer at the attractor representatives (for a 2-D image). A 3-D slice
(a label cube with one degenerate counts == 1 axis, as
:func:basins_of_attraction paints when imaging a slice of a
higher-dimensional flow) is squeezed to its two non-degenerate axes so it
renders as a 2-D image; a genuinely 3-D label cube keeps all three axes
on the spec.
The image shares the attractor palette (tab20) with
:meth:AttractorSet.to_plot_spec: the explicit {id: swatch index}
mapping is recorded in meta["palette_index"] (identical to the one the
scatter carries), so a given attractor id is the same colour in both
views; meta["palette"] / meta["diverged_color"] name the colormap
and the fixed escape colour. The :mod:tsdynamics.viz.spec import is
lazy, so building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/basins/basins.py
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
basin_fractions
¶
basin_fractions(
system: Any,
region: Box | Ball | Grid,
*,
n: int = 10000,
resolution: int | tuple[int, ...] = 100,
seed: int | None = 0,
dt: float = 1.0,
max_steps: int = 10000,
merge_tol: float | None = None,
**fsm: Any,
) -> BasinFractions
Estimate basin stability: each attractor's share of a sampled region.
Draw n random initial conditions from region and classify each; the
fraction converging to an attractor estimates its basin stability (Menck et
al., 2013). The estimate is dimension-free — its standard error
:math:\sqrt{p(1-p)/n} depends only on the fraction and n.
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A discrete map or continuous flow.
TYPE:
|
region
|
The measure to sample initial conditions from (uniform over a Box/Ball, or a Grid's bounding box).
TYPE:
|
n
|
Number of random initial conditions.
TYPE:
|
resolution
|
Recurrence cells per axis (a Grid uses its own
TYPE:
|
seed
|
Seed for the sampler (reproducible).
TYPE:
|
dt
|
Integration step between cell checks for a flow (ignored for a map).
TYPE:
|
max_steps
|
Per-sample step cap before declaring divergence.
TYPE:
|
merge_tol
|
Merge attractors whose centroids lie within this distance.
TYPE:
|
**fsm
|
Finite-state-machine thresholds forwarded to
:class:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
BasinFractions
|
Fractions per attractor, the diverged share, and the attractors. |
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
References
P. J. Menck, J. Heitzig, N. Marwan and J. Kurths, "How basin stability complements the linear-stability paradigm", Nature Physics 9, 89 (2013).
Source code in src/tsdynamics/analysis/basins/basins.py
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 | |
BasinFractions
dataclass
¶
BasinFractions(
fractions: dict[int, float] = dict(),
diverged: float = 0.0,
n: int = 0,
attractors: AttractorSet = AttractorSet(),
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Monte-Carlo basin stability: each attractor's share of a sampled region.
| ATTRIBUTE | DESCRIPTION |
|---|---|
fractions |
Attractor id → fraction of sampled initial conditions converging to it. |
diverged |
Fraction of samples that diverged / never settled.
TYPE:
|
n |
Number of initial conditions sampled.
TYPE:
|
attractors |
The attractors the ids refer to.
TYPE:
|
standard_error
property
¶
Binomial standard error :math:\sqrt{p(1-p)/n} per fraction.
dominant
property
¶
dominant: int | None
Id of the attractor with the largest basin (None if all diverged).
to_plot_spec
¶
Describe the basin fractions as a backend-agnostic :class:PlotSpec.
Builds a CATEGORICAL_BAR — one BAR per attractor id (plus a final
bar for the diverged share when it is non-zero) over a categorical x-axis
whose :attr:~tsdynamics.viz.spec.Axis.categories carry the labels
(attractor 1, …, diverged). The "cat" channel holds the
integer category index for each bar and "y" its basin fraction. The
bars are coloured from the shared attractor palette (tab20, recorded
in meta["palette"]), the diverged bar in the fixed diverged colour, so
an id keeps its colour across the basin views. The
:mod:tsdynamics.viz.spec import is lazy, so building a spec never pulls a
plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/basins/basins.py
Boundary structure¶
basin_entropy
¶
basin_entropy(
basins: Any,
*,
box_size: int = 5,
base: float = e,
include_diverged: bool = False,
) -> BasinEntropy
Basin entropy :math:S_b and boundary basin entropy :math:S_{bb}.
Partition the basin image into non-overlapping boxes of box_size cells per
axis. Within box :math:i, with colour fractions :math:p_{ij}, the Gibbs
entropy is :math:S_i = -\sum_j p_{ij}\log p_{ij}. Then
:math:S_b = \langle S_i\rangle over all boxes and
:math:S_{bb} = \langle S_i\rangle over boundary boxes (more than one
colour). :math:S_{bb} > \log 2 is sufficient for a fractal boundary, since
a box straddling a smooth boundary holds at most two colours.
| PARAMETER | DESCRIPTION |
|---|---|
basins
|
The basin image (attractor ids
TYPE:
|
box_size
|
Box side length in cells.
TYPE:
|
base
|
Logarithm base. With the default natural log the fractal threshold is
:math:
TYPE:
|
include_diverged
|
If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
BasinEntropy
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
A. Daza, A. Wagemakers, B. Georgeot, D. Guéry-Odelin and M. A. F. Sanjuán, "Basin entropy: a new tool to analyze uncertainty in dynamical systems", Scientific Reports 6, 31416 (2016).
Source code in src/tsdynamics/analysis/basins/metrics.py
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | |
BasinEntropy
dataclass
¶
BasinEntropy(
sb: float = 0.0,
sbb: float = 0.0,
n_boxes: int = 0,
n_boundary_boxes: int = 0,
box_size: int = 0,
log_base: float = 0.0,
fractal_boundary: bool = False,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Basin entropy and boundary basin entropy of a basin diagram.
| ATTRIBUTE | DESCRIPTION |
|---|---|
sb |
Basin entropy :math:
TYPE:
|
sbb |
Boundary basin entropy :math:
TYPE:
|
n_boxes |
Number of boxes the grid was partitioned into.
TYPE:
|
n_boundary_boxes |
Number of boxes containing more than one basin.
TYPE:
|
box_size |
Box side length in cells.
TYPE:
|
log_base |
Base of the logarithm (
TYPE:
|
fractal_boundary |
TYPE:
|
uncertainty_exponent
¶
uncertainty_exponent(
basins: Any,
*,
radii: tuple[int, ...] = (1, 2, 3, 4, 5),
cell_size: float | ndarray = 1.0,
include_diverged: bool = False,
) -> UncertaintyExponent
Estimate the uncertainty exponent of a basin boundary.
A cell is :math:\varepsilon-uncertain when at least one axis-neighbour at
distance :math:\varepsilon carries a different basin label. The fraction of
such cells scales as :math:f(\varepsilon)\sim\varepsilon^{\alpha}
(Grebogi et al., 1983); :math:\alpha is the slope of a log-log fit and the
boundary box-counting dimension is :math:D_0 = D - \alpha.
| PARAMETER | DESCRIPTION |
|---|---|
basins
|
The basin image.
TYPE:
|
radii
|
Perturbation radii in cells.
TYPE:
|
cell_size
|
Physical cell spacing (a scalar or per-axis); rescales |
include_diverged
|
If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
UncertaintyExponent
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If fewer than two |
References
C. Grebogi, S. W. McDonald, E. Ott and J. A. Yorke, "Final state sensitivity: an obstruction to predictability", Physics Letters A 99, 415 (1983).
Source code in src/tsdynamics/analysis/basins/metrics.py
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 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 | |
UncertaintyExponent
dataclass
¶
UncertaintyExponent(
alpha: float = 0.0,
boundary_dimension: float = 0.0,
state_dimension: int = 0,
epsilons: ndarray = (lambda: empty(0))(),
f: ndarray = (lambda: empty(0))(),
r_squared: float = 0.0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
The uncertainty exponent of a basin boundary.
| ATTRIBUTE | DESCRIPTION |
|---|---|
alpha |
Uncertainty exponent :math:
TYPE:
|
boundary_dimension |
Box-counting dimension of the boundary, :math:
TYPE:
|
state_dimension |
State-space (grid) dimension :math:
TYPE:
|
epsilons |
Perturbation radii used (in state-space units).
TYPE:
|
f |
Fraction of :math:
TYPE:
|
r_squared |
Coefficient of determination of the log-log fit.
TYPE:
|
to_plot_spec
¶
Describe the uncertainty exponent as its log--log scaling fit.
The uncertainty exponent is a scaling estimate:
:math:f(\varepsilon)\sim\varepsilon^{\alpha} (Grebogi et al., 1983), so
the natural figure is the SCALING_FIT of :math:\log f against
:math:\log\varepsilon with the fitted slope :math:\alpha. This builds
that spec directly — a SCATTER of the curve, the fit region marked,
and the fit line drawn from the slope :math:\alpha and an intercept
recovered from the curve mean — so a single result.plot.scaling()
renders it like every other dimension / Lyapunov-from-data scaling result.
The :mod:tsdynamics.viz.spec import is lazy, so building a spec never
pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/basins/metrics.py
wada_property
¶
wada_property(
basins: Any,
*,
radii: tuple[int, ...] = (1, 2, 3, 4, 5),
threshold: float = 0.9,
include_diverged: bool = False,
) -> WadaResult
Test a basin diagram for the Wada property on a grid.
With three or more basins, a boundary cell is "Wada-complete" at radius
:math:r when its Chebyshev-:math:r neighbourhood contains every basin
colour. The fraction :math:W(r) of such boundary cells tends to one for a
genuine Wada boundary (Daza et al., 2015). This is a sufficient grid test,
not a topological proof.
| PARAMETER | DESCRIPTION |
|---|---|
basins
|
The basin image. Diverged cells (
TYPE:
|
radii
|
Chebyshev radii (in cells) at which to grow the neighbourhood.
TYPE:
|
threshold
|
Minimum :math:
TYPE:
|
include_diverged
|
If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
WadaResult
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If |
References
A. Daza, A. Wagemakers, M. A. F. Sanjuán and J. A. Yorke, "Testing for basins of Wada", Scientific Reports 5, 16579 (2015).
Source code in src/tsdynamics/analysis/basins/metrics.py
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 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 | |
WadaResult
dataclass
¶
WadaResult(
is_wada: bool = False,
n_basins: int = 0,
radii: ndarray = (lambda: empty(0))(),
fractions: ndarray = (lambda: empty(0))(),
n_boundary_cells: int = 0,
threshold: float = 0.0,
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
A grid test for the Wada property of a basin diagram.
| ATTRIBUTE | DESCRIPTION |
|---|---|
is_wada |
TYPE:
|
n_basins |
Number of attractor basins (colours) considered.
TYPE:
|
radii |
Chebyshev radii tested.
TYPE:
|
fractions |
Fraction of boundary cells whose neighbourhood contains every basin, per
radius (:math:
TYPE:
|
n_boundary_cells |
Number of boundary cells.
TYPE:
|
threshold |
Acceptance fraction at the largest radius.
TYPE:
|
resilience
¶
resilience(
result: BasinsResult, attractor_id: int
) -> ScalarResult
Minimal-fatal-shock resilience of an attractor: its distance to the boundary.
The smallest perturbation that pushes the attractor out of its own basin, estimated as the state-space distance from the attractor's representative to the nearest cell of another basin (Halekotte & Feudel, 2020). Larger means more resilient.
| PARAMETER | DESCRIPTION |
|---|---|
result
|
A basin image carrying its grid and attractors.
TYPE:
|
attractor_id
|
Which attractor (basin label) to measure.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ScalarResult
|
Distance from the attractor to its basin boundary (behaves as a
|
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
ValueError
|
If the labels are not laid out on the grid (a pre-squeezed slice), or
|
Notes
A sliced basin image (a label cube with one or more degenerate counts == 1
axes, e.g. a position-plane slice of a higher-dimensional flow) is collapsed to
its free axes before the distance transform, so a pinned axis does not inject a
spurious one-cell-away boundary that would cap the reported distance.
References
L. Halekotte and U. Feudel, "Minimal fatal shocks in multistable complex networks", Scientific Reports 10, 11374 (2020). Builds on the integral stability of C. Mitra, J. Kurths and R. V. Donner, Scientific Reports 5, 16196 (2015).
Source code in src/tsdynamics/analysis/basins/metrics.py
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 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 | |
Continuation & tipping¶
continuation
¶
continuation(
system: Any,
param: str,
values: Any,
region: Box | Ball | Grid,
*,
n: int = 2000,
resolution: int | tuple[int, ...] = 100,
seed: int | None = 0,
dt: float = 1.0,
max_steps: int = 10000,
min_fraction: float = 0.0,
match_method: str = "centroid",
match_threshold: float | None = None,
**fsm: Any,
) -> ContinuationResult
Track attractors and basin fractions as param sweeps values.
At each parameter value the attractors are found and their basin fractions
estimated (:func:~tsdynamics.analysis.basins.basin_fractions); consecutive
value's attractors are matched greedily by nearest state-space distance so a
persisting attractor keeps its id and a vanishing one drops out (Datseris,
Rossi & Wagemakers, 2023).
| PARAMETER | DESCRIPTION |
|---|---|
system
|
A discrete map or continuous flow exposing
TYPE:
|
param
|
Name of the parameter to sweep.
TYPE:
|
values
|
Parameter values, in the order to walk them.
TYPE:
|
region
|
The region whose basin fractions are measured at each value.
TYPE:
|
n
|
Initial conditions sampled per value.
TYPE:
|
resolution
|
Recurrence cells per axis (a Grid uses its own
TYPE:
|
seed
|
Sampler seed (shared across values for a fair comparison).
TYPE:
|
dt
|
Integration step between cell checks for a flow.
TYPE:
|
max_steps
|
Per-sample step cap.
TYPE:
|
min_fraction
|
Drop attractors whose basin fraction is below this at a given value before
matching — a filter for the tiny spurious sets the recurrence finder can
report near unstable equilibria.
TYPE:
|
match_method
|
Set distance used to match attractors between values.
TYPE:
|
match_threshold
|
Reject a match farther apart than this (so an attractor that jumps is not
spuriously tied to a different one).
TYPE:
|
**fsm
|
Finite-state-machine thresholds forwarded to the recurrence finder.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
ContinuationResult
|
|
| RAISES | DESCRIPTION |
|---|---|
TypeError
|
If |
References
G. Datseris, K. L. Rossi and A. Wagemakers, "Framework for global stability analysis of dynamical systems", Chaos 33, 073151 (2023).
Source code in src/tsdynamics/analysis/basins/continuation.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | |
ContinuationResult
dataclass
¶
ContinuationResult(
param: str = "",
values: ndarray = (lambda: empty(0))(),
fractions: dict[int, ndarray] = dict(),
attractors: list[dict[int, Attractor]] = list(),
diverged: ndarray = (lambda: empty(0))(),
*,
meta: Mapping[str, Any] = dict(),
)
Bases: AnalysisResult
Attractors and basin fractions tracked across a parameter sweep.
| ATTRIBUTE | DESCRIPTION |
|---|---|
param |
The swept parameter name.
TYPE:
|
values |
Parameter values, in sweep order.
TYPE:
|
fractions |
Global attractor id → basin fraction at each value ( |
attractors |
Per value, the located attractors keyed by their global (matched) id. |
diverged |
Diverged-or-untracked fraction at each value: the diverged share plus
the basin mass of any attractor dropped by
TYPE:
|
tipping_points
¶
tipping_points(
*, threshold: float = 0.0
) -> CollectionResult
Tipping events along this continuation (see :func:tipping_points).
to_plot_spec
¶
Describe the continuation as a backend-agnostic :class:PlotSpec.
Builds a CONTINUATION spec — the basin fractions stacked against
the swept parameter, one filled AREA band per global attractor id (its
"lo" / "hi" channels are the cumulative fraction below and above
the band). The attractor bands fill the tracked share; the remaining gap
up to 1 is the diverged / untracked mass (:attr:diverged), so the
bands plus that gap tile :math:[0, 1]. Each tipping event from
:meth:tipping_points — a basin annihilating ("disappear") or being
born ("appear") — is drawn as a vertical
:class:~tsdynamics.viz.spec.Annotation at the parameter value where it
happens. The bands share the attractor palette (tab20, recorded in
meta["palette"]) so an id keeps its colour across the basin views. A
:class:~tsdynamics.viz.spec.Legend is attached when more than one
attractor is tracked. The :mod:tsdynamics.viz.spec import is lazy, so
building a spec never pulls a plotting library.
| PARAMETER | DESCRIPTION |
|---|---|
kind
|
Override the semantic kind (e.g.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
PlotSpec
|
|
Source code in src/tsdynamics/analysis/basins/continuation.py
tipping_points
¶
tipping_points(
result: ContinuationResult, *, threshold: float = 0.0
) -> CollectionResult
Read tipping events off a continuation.
A tipping event is a basin fraction crossing threshold between consecutive
parameter values: a disappear event (fraction falls to/through it — an
attractor and its basin annihilating) or an appear event (a new attractor
gaining a basin). With the default threshold=0 only true
appearance/annihilation is reported; raise it to flag basins shrinking past a
safety margin.
| PARAMETER | DESCRIPTION |
|---|---|
result
|
A continuation from :func:
TYPE:
|
threshold
|
Basin-fraction level whose crossing counts as a tipping event.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
CollectionResult
|
Behaves as a |
Source code in src/tsdynamics/analysis/basins/continuation.py
Sampling¶
Sagitta-based tools for time-ordered samples: choose an output dt (the
largest stride whose mid-point bow off the chord — the sagitta — stays under a
geometric tolerance), or read the per-point sagitta as a local "how sharply it
bends" field.
estimate_dt_from_sagitta
¶
estimate_dt_from_sagitta(
y: ndarray,
dt0: float,
*,
epsilon: float,
percentile: float = 95.0,
coarsen_only: bool = True,
use_relative: bool | None = None,
min_points_per_segment: int = 3,
search_growth: float = 1.5,
) -> SagittaDt
Sagitta-based output-step :math:\Delta t^\ast selector.
A derivative-free, scale-invariant, idempotent heuristic for the output
sampling step. For a range of candidate strides it measures the sagitta —
the perpendicular bow of the midpoint of each (i-span, i, i+span) triple
off its chord — at a robust percentile, then picks the largest stride whose
sagitta still satisfies the geometric tolerance epsilon.
A one-dimensional input is first delay-embedded so the geometric criterion
lives in a reconstructed state space: the delay is the first minimum of the
time-delayed mutual information (Fraser & Swinney 1986) and the dimension is
Kennel's false-nearest-neighbour estimate (Kennel, Brown & Abarbanel 1992),
both via the public :mod:tsdynamics.analysis.embedding estimators
(:func:~tsdynamics.analysis.embedding.optimal_delay and
:func:~tsdynamics.analysis.embedding.embedding_dimension with
method="fnn"). Multivariate input is used directly, per-feature
σ-normalised (ddof=1).
| PARAMETER | DESCRIPTION |
|---|---|
y
|
Time-ordered samples. A 1-D (or
TYPE:
|
dt0
|
Base sampling step :math:
TYPE:
|
epsilon
|
Geometric tolerance :math:
TYPE:
|
percentile
|
Robust percentile :math:
TYPE:
|
coarsen_only
|
If
TYPE:
|
use_relative
|
Whether to use the relative sagitta criterion
(
TYPE:
|
min_points_per_segment
|
Minimum number of triples
TYPE:
|
search_growth
|
Multiplicative growth factor (
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
SagittaDt
|
The chosen :math: |
| RAISES | DESCRIPTION |
|---|---|
InvalidParameterError
|
If |
References
A. M. Fraser and H. L. Swinney, "Independent coordinates for strange attractors from mutual information", Phys. Rev. A 33, 1134 (1986).
M. B. Kennel, R. Brown and H. D. I. Abarbanel, "Determining embedding dimension for phase-space reconstruction using a geometrical construction", Phys. Rev. A 45, 3403 (1992).
Source code in src/tsdynamics/analysis/sampling/sagitta.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 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 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 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 | |
sagitta_profile
¶
sagitta_profile(
samples: ndarray,
*,
span: int = 1,
relative: bool = True,
normalize: bool = True,
) -> ndarray
Per-point sagitta along a sampled curve — its local bow off the chord.
For each interior point i the sagitta is the perpendicular distance of
samples[i] from the chord through its neighbours (i-span, i+span) (the
same geometry the sagitta-based Δt selector uses, here evaluated at every
point rather than a strided percentile). Large where the trajectory bends
sharply, ~0 on straight runs; the first/last span points are 0.
| PARAMETER | DESCRIPTION |
|---|---|
samples
|
Time-ordered points (a 1-D series is treated as a column).
TYPE:
|
span
|
Neighbour offset of the chord
TYPE:
|
relative
|
Divide each bow by its chord length, giving a dimensionless bend ratio
that is (largely) independent of the local speed/step. Default
TYPE:
|
normalize
|
σ-normalize each feature first (per-feature std,
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
(ndarray, shape(n))
|
The per-point sagitta, aligned to |