tsdynamics 5.2.5__tar.gz → 5.2.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tsdynamics-5.2.6/PKG-INFO +221 -0
- tsdynamics-5.2.6/README.md +185 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/marshal.rs +5 -2
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/basin.rs +13 -5
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/ensemble.rs +8 -4
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/op.rs +6 -2
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/dop853.rs +8 -3
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/registry.rs +58 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/solver.rs +9 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/pyproject.toml +4 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/__init__.py +1 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_base.py +1 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_scaling.py +30 -9
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/metrics.py +17 -3
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/expansion.py +1 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/zero_one.py +7 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/_common.py +1 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/correlation.py +12 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/delay.py +23 -8
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/dimension.py +13 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/sample.py +11 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/_interval.py +54 -8
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/periodic.py +9 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/lyapunov/__init__.py +28 -5
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/lyapunov/from_data.py +10 -2
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/orbit_diagram.py +52 -5
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/sampling/sagitta.py +24 -6
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/_common.py +15 -7
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/projected.py +34 -16
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/tangent.py +46 -4
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/events.py +10 -3
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/continuous.py +80 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/discrete.py +36 -2
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/wrapped.py +4 -3
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/plugins.py +4 -3
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/chaotic_attractors.py +6 -4
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/climate_geophysics.py +11 -2
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/exotic_systems.py +6 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/oscillatory_systems.py +18 -4
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/chaotic_maps.py +4 -6
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/exotic_maps.py +14 -8
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/geometric_maps.py +10 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/spectral.py +3 -1
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/compose.py +23 -6
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_core.py +78 -8
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_anim.py +80 -9
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/threejs/_lower.py +7 -5
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/spec.py +8 -2
- tsdynamics-5.2.5/PKG-INFO +0 -160
- tsdynamics-5.2.5/README.md +0 -124
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/LICENSE +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/Cargo.lock +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/basin.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/dde.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/events.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/lyapunov.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/map.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/map_lyapunov.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/mod.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/ode.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/sde.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/stepper.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/dde.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/event.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/integrate.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/lyapunov.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/map.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/map_lyapunov.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/param_sweep.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/problem.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/rng.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/sde.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/testkit.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/builder.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/eval.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/reference.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/tape.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/coverage_tape.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/CircadianRhythm.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/Lorenz.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/SanUmSrisuchinwong.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/SprottMore.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/Torus.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/YuWang2.txt +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/gen_fixtures.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/roundtrip.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/codegen.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/error.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/evaluator.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/shims.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/compile_latency.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/interpreter_equivalence.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/caps.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/adams.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/bs3.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/cashkarp.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/control.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/euler.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/heun.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/heun_euler.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/midpoint.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/mod.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/ralston.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk4.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk45.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk4_38.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rkf45.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/ssprk3.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/testkit.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/tsit5.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/backward_euler.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/bdf.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/control.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/implicit_midpoint.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/linalg.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/mod.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/newton.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/rosenbrock.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/sdirk2.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/trapezoid.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/trbdf2.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/euler_maruyama.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/milstein.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/mod.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/testkit.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/explicit_accuracy.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/implicit_stiff.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/registry.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/Cargo.toml +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/src/interp.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/src/lib.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/tests/oracle_equivalence.rs +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/_result_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_plotbuilder.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_array.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_collection.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_json.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_scalar.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_viz.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_tangent.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/attractors.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/basins.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/continuation.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/gali.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/_scaling.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/fixedmass.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/generalized.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/embed.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/core.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/dispersion.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/lz.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/multiscale.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/permutation.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/fixed.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/poincare.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/return_map.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/matrix.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/rqa.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/windowed.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/sampling/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/generators.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/hypothesis.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/statistics.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/sampling.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/trajectory.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_base.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_crossings.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_variational.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/ensemble.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/poincare.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/stroboscopic.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/wrapped.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/_families.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/compile.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/problem.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/reference.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/run.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/run_methods.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/sde_run.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/stepper.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/symbols.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/errors.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_accessors.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_dde_lyapunov.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_plottable.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/base.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/delay.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/protocol.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/stochastic.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/py.typed +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/registry.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/explicit.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/implicit.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/select.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/stochastic.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/chem_bio_systems.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/coupled_systems.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/delayed_systems.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/physical_systems.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/population_dynamics.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/spatial_fields.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/polynomial_maps.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/population_maps.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/_common.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/_result.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/features.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/preprocessing.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/utils/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/utils/grids.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/export.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/producers.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/caps.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/json.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_anim.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_threed.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_composite.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_core.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_html.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_threed.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/threejs/__init__.py +0 -0
- {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/style.py +0 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tsdynamics
|
|
3
|
+
Version: 5.2.6
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Science/Research
|
|
6
|
+
Classifier: Operating System :: OS Independent
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
11
|
+
Classifier: Typing :: Typed
|
|
12
|
+
Requires-Dist: numpy>=2.0,<3.0
|
|
13
|
+
Requires-Dist: scipy>=1.14,<2.0
|
|
14
|
+
Requires-Dist: symengine>=0.13,<1.0
|
|
15
|
+
Requires-Dist: sympy>=1.13,<2.0
|
|
16
|
+
Requires-Dist: plotly>=6,<7 ; extra == 'interactive'
|
|
17
|
+
Requires-Dist: lzcomplexity>=0.9 ; extra == 'lz'
|
|
18
|
+
Requires-Dist: matplotlib>=3.9,<5 ; extra == 'plot'
|
|
19
|
+
Requires-Dist: matplotlib>=3.9,<5 ; extra == 'viz'
|
|
20
|
+
Provides-Extra: interactive
|
|
21
|
+
Provides-Extra: lz
|
|
22
|
+
Provides-Extra: plot
|
|
23
|
+
Provides-Extra: viz
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Summary: Zero-warmup Rust-engine ODE/DDE/SDE integration, discrete maps, and chaos analysis for dynamical systems.
|
|
26
|
+
Keywords: dynamical systems,ODE,DDE,chaos,Lyapunov exponents,nonlinear dynamics,bifurcation,attractors,reservoir computing
|
|
27
|
+
Author-email: Daniel Estevez <kemossabee@gmail.com>
|
|
28
|
+
License-Expression: MIT
|
|
29
|
+
Requires-Python: >=3.12, <4.0
|
|
30
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
31
|
+
Project-URL: Changelog, https://github.com/El3ssar/TSDynamics/releases
|
|
32
|
+
Project-URL: Documentation, https://el3ssar.github.io/TSDynamics/
|
|
33
|
+
Project-URL: Issues, https://github.com/El3ssar/TSDynamics/issues
|
|
34
|
+
Project-URL: Source, https://github.com/El3ssar/TSDynamics
|
|
35
|
+
|
|
36
|
+
# TSDynamics
|
|
37
|
+
|
|
38
|
+
[](https://pypi.org/project/tsdynamics/)
|
|
39
|
+
[](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml)
|
|
40
|
+
[](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml)
|
|
41
|
+
[](https://el3ssar.github.io/TSDynamics/)
|
|
42
|
+
[](https://pypi.org/project/tsdynamics/)
|
|
43
|
+
[](https://codecov.io/gh/El3ssar/TSDynamics)
|
|
44
|
+
|
|
45
|
+
**Dynamical systems in Python: 151 built-in systems, a native Rust integration
|
|
46
|
+
engine, and a chaos-analysis toolkit — with the simplest system-definition
|
|
47
|
+
contract anywhere.**
|
|
48
|
+
|
|
49
|
+
You write the math (one symbolic method); TSDynamics lowers it to a native Rust
|
|
50
|
+
engine and handles integration, Lyapunov spectra, bifurcation diagrams, Poincaré
|
|
51
|
+
sections, attractors & basins — and even the documentation page for your system.
|
|
52
|
+
|
|
53
|
+
<p align="center">
|
|
54
|
+
<img src="docs/assets/readme/lorenz_spin.gif" width="440" alt="A spinning Lorenz attractor"><br>
|
|
55
|
+
<em>A built-in Lorenz attractor — integrated, spun, and saved to a GIF (code below).</em>
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
import tsdynamics as ts
|
|
60
|
+
|
|
61
|
+
lor = ts.Lorenz()
|
|
62
|
+
traj = lor.integrate(final_time=100.0, dt=0.01)
|
|
63
|
+
traj["x"] # named component access
|
|
64
|
+
lor.lyapunov_spectrum() # → [0.91, ~0, -14.57]
|
|
65
|
+
ts.kaplan_yorke_dimension(_) # → ~2.06
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
📖 **Documentation: <https://el3ssar.github.io/TSDynamics/>**
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Define your own system
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
import tsdynamics as ts
|
|
76
|
+
|
|
77
|
+
class Rossler(ts.ContinuousSystem):
|
|
78
|
+
params = {"a": 0.2, "b": 0.2, "c": 5.7}
|
|
79
|
+
dim = 3
|
|
80
|
+
variables = ("x", "y", "z") # optional niceties
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _equations(y, t, *, a, b, c):
|
|
84
|
+
x, yv, z = y(0), y(1), y(2)
|
|
85
|
+
return (-yv - z, x + a * yv, b + z * (x - c))
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
That's the whole contract. The class auto-registers: every analysis tool works
|
|
89
|
+
on it, the test-suite sweeps it, and the docs build renders its equations
|
|
90
|
+
(LaTeX, straight from the symbolics) and its attractor — zero extra steps. Delay
|
|
91
|
+
systems use `y(0, t - tau)`; maps implement `_step`/`_jacobian` (signature order
|
|
92
|
+
validated at import); SDEs add a `_diffusion` term.
|
|
93
|
+
|
|
94
|
+
## From equations to figures
|
|
95
|
+
|
|
96
|
+
Every system produces a `Trajectory`, and every `Trajectory` knows how to plot
|
|
97
|
+
itself (`to_plot_spec` auto-detects the right kind from the data). The plot is a
|
|
98
|
+
backend-neutral *spec* you tweak fluently, then `render` or `save` to matplotlib,
|
|
99
|
+
plotly (interactive), or a three.js / JSON export.
|
|
100
|
+
|
|
101
|
+
**Bifurcation diagram of the logistic map**, with the period-doubling onsets
|
|
102
|
+
marked — `orbit_diagram` is one call, and `.bifurcation_points()` finds the
|
|
103
|
+
cascade ($r_1 = 3$, $r_2 = 1 + \sqrt6 \approx 3.449$, …):
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import numpy as np, tsdynamics as ts
|
|
107
|
+
from tsdynamics.viz import Annotation
|
|
108
|
+
|
|
109
|
+
orbit = ts.orbit_diagram(ts.Logistic(), "r", np.linspace(2.8, 4.0, 2000))
|
|
110
|
+
pts = orbit.bifurcation_points()
|
|
111
|
+
|
|
112
|
+
spec = orbit.to_plot_spec().relabel(x="r", y="x*", title="Logistic bifurcation")
|
|
113
|
+
spec.style(color="k", s=0.2, alpha=0.5) # tiny semi-transparent dots
|
|
114
|
+
spec.annotations = [
|
|
115
|
+
Annotation("vline", x=pts[i], text=lbl, style={"color": "red", "linestyle": "--"})
|
|
116
|
+
for i, lbl in [(0, " r₁"), (1, " r₂"), (3, " r₄")]
|
|
117
|
+
]
|
|
118
|
+
spec.save("bifurcation.png", size=(1600, 700))
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+

|
|
122
|
+
|
|
123
|
+
**A PDE, too** — the Kuramoto–Sivashinsky equation is a built-in spatially
|
|
124
|
+
extended system; its space–time field is auto-detected and drawn as a heatmap:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
ks = ts.KuramotoSivashinsky(N=128, L=22.0)
|
|
128
|
+
traj = ks.integrate(final_time=200.0, dt=0.25)
|
|
129
|
+
traj.to_plot_spec().save("ks.png") # 128-mode space–time field
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
<p align="center">
|
|
135
|
+
<img src="docs/assets/readme/ks_spacetime.png" alt="Kuramoto–Sivashinsky space–time field">
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
The spinning attractor at the top is the same `to_plot_spec`, animated:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
traj = ts.Lorenz().integrate(final_time=100.0, dt=0.01)
|
|
143
|
+
spec = traj.to_plot_spec()
|
|
144
|
+
spec.style(axes=False).trail(None).camera(spin=0.4) # full curve, no axes, rotate
|
|
145
|
+
spec.animate(fps=30, duration=10, loop=True)
|
|
146
|
+
spec.save("lorenz.gif")
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## A taste of the analysis layer
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import numpy as np, tsdynamics as ts
|
|
153
|
+
|
|
154
|
+
# Poincaré section of the Rössler attractor (root-refined crossings)
|
|
155
|
+
section = ts.poincare_section(ts.Rossler(), plane=("y", 0.0, "up"), n=500)
|
|
156
|
+
|
|
157
|
+
# Fixed points of the Hénon map, with stability
|
|
158
|
+
ts.fixed_points(ts.Henon())
|
|
159
|
+
# [FixedPoint([-1.1314 -0.3394], unstable), FixedPoint([0.6314 0.1894], unstable)]
|
|
160
|
+
|
|
161
|
+
# Maximal Lyapunov exponent — no Jacobian needed
|
|
162
|
+
ts.max_lyapunov(ts.Lorenz(ic=[1, 1, 1]), dt=0.05) # ≈ 0.9
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Plus: **attractors & basins** of any flow or map, correlation/Rényi **fractal
|
|
166
|
+
dimensions**, **permutation/sample/dispersion entropy**, **RQA** (recurrence
|
|
167
|
+
quantification), **surrogate** hypothesis tests, **delay embedding** (Takens,
|
|
168
|
+
optimal τ, Cao/FNN), GALI & the 0–1 chaos test, and Lyapunov exponents **from a
|
|
169
|
+
bare time series** (Kantz/Rosenstein).
|
|
170
|
+
|
|
171
|
+
## Highlights
|
|
172
|
+
|
|
173
|
+
- **Three families, one interface**
|
|
174
|
+
|
|
175
|
+
- **ODEs**
|
|
176
|
+
|
|
177
|
+
- **DDEs**
|
|
178
|
+
- **SDEs**
|
|
179
|
+
- **Discrete Maps**
|
|
180
|
+
|
|
181
|
+
- **151 built-in systems** with literature parameters.
|
|
182
|
+
|
|
183
|
+
- **Native engine**: equations lower to a Rust engine (an SSA-tape
|
|
184
|
+
interpreter, with a Cranelift JIT alongside) in-process; parameters are runtime
|
|
185
|
+
values, so changing them is free and there is no compile step or cache.
|
|
186
|
+
|
|
187
|
+
- **Composition** — a `PoincareMap` of a flow *is* a discrete map, so
|
|
188
|
+
`orbit_diagram(PoincareMap(Rossler(), ("y", 0.0)), "c", values)` draws the
|
|
189
|
+
bifurcation diagram of a *flow* in one line.
|
|
190
|
+
|
|
191
|
+
- **Backend-neutral plotting** — one `PlotSpec` IR renders to matplotlib, plotly
|
|
192
|
+
(interactive + animated HTML), three.js, or JSON, with a fluent styling/theming
|
|
193
|
+
vocabulary.
|
|
194
|
+
|
|
195
|
+
## Install
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
pip install tsdynamics # or: uv add tsdynamics
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
A prebuilt `abi3` wheel (manylinux / musllinux / macOS / Windows) bundles the
|
|
202
|
+
native Rust engine. No Rust toolchain and no C compiler needed to install or
|
|
203
|
+
run. Optional plotting extra: `tsdynamics[plot,interactive]` (matplotlib, plotly).
|
|
204
|
+
|
|
205
|
+
## Development
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
git clone https://github.com/El3ssar/TSDynamics && cd TSDynamics
|
|
209
|
+
uv sync --group dev --group docs
|
|
210
|
+
uv run pytest -m "not slow" --no-cov # fast tier
|
|
211
|
+
uv run pytest --no-cov # full local suite
|
|
212
|
+
TSD_DOCS_FIGURES=0 uv run mkdocs serve # docs preview
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Releases are automated: conventional-commit PR titles drive
|
|
216
|
+
[semantic-release](https://python-semantic-release.readthedocs.io/) on merge see [CONTRIBUTING](https://el3ssar.github.io/TSDynamics/project/contributing/).
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT © Daniel Estevez
|
|
221
|
+
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# TSDynamics
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/tsdynamics/)
|
|
4
|
+
[](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml)
|
|
6
|
+
[](https://el3ssar.github.io/TSDynamics/)
|
|
7
|
+
[](https://pypi.org/project/tsdynamics/)
|
|
8
|
+
[](https://codecov.io/gh/El3ssar/TSDynamics)
|
|
9
|
+
|
|
10
|
+
**Dynamical systems in Python: 151 built-in systems, a native Rust integration
|
|
11
|
+
engine, and a chaos-analysis toolkit — with the simplest system-definition
|
|
12
|
+
contract anywhere.**
|
|
13
|
+
|
|
14
|
+
You write the math (one symbolic method); TSDynamics lowers it to a native Rust
|
|
15
|
+
engine and handles integration, Lyapunov spectra, bifurcation diagrams, Poincaré
|
|
16
|
+
sections, attractors & basins — and even the documentation page for your system.
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<img src="docs/assets/readme/lorenz_spin.gif" width="440" alt="A spinning Lorenz attractor"><br>
|
|
20
|
+
<em>A built-in Lorenz attractor — integrated, spun, and saved to a GIF (code below).</em>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import tsdynamics as ts
|
|
25
|
+
|
|
26
|
+
lor = ts.Lorenz()
|
|
27
|
+
traj = lor.integrate(final_time=100.0, dt=0.01)
|
|
28
|
+
traj["x"] # named component access
|
|
29
|
+
lor.lyapunov_spectrum() # → [0.91, ~0, -14.57]
|
|
30
|
+
ts.kaplan_yorke_dimension(_) # → ~2.06
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
📖 **Documentation: <https://el3ssar.github.io/TSDynamics/>**
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Define your own system
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import tsdynamics as ts
|
|
41
|
+
|
|
42
|
+
class Rossler(ts.ContinuousSystem):
|
|
43
|
+
params = {"a": 0.2, "b": 0.2, "c": 5.7}
|
|
44
|
+
dim = 3
|
|
45
|
+
variables = ("x", "y", "z") # optional niceties
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def _equations(y, t, *, a, b, c):
|
|
49
|
+
x, yv, z = y(0), y(1), y(2)
|
|
50
|
+
return (-yv - z, x + a * yv, b + z * (x - c))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
That's the whole contract. The class auto-registers: every analysis tool works
|
|
54
|
+
on it, the test-suite sweeps it, and the docs build renders its equations
|
|
55
|
+
(LaTeX, straight from the symbolics) and its attractor — zero extra steps. Delay
|
|
56
|
+
systems use `y(0, t - tau)`; maps implement `_step`/`_jacobian` (signature order
|
|
57
|
+
validated at import); SDEs add a `_diffusion` term.
|
|
58
|
+
|
|
59
|
+
## From equations to figures
|
|
60
|
+
|
|
61
|
+
Every system produces a `Trajectory`, and every `Trajectory` knows how to plot
|
|
62
|
+
itself (`to_plot_spec` auto-detects the right kind from the data). The plot is a
|
|
63
|
+
backend-neutral *spec* you tweak fluently, then `render` or `save` to matplotlib,
|
|
64
|
+
plotly (interactive), or a three.js / JSON export.
|
|
65
|
+
|
|
66
|
+
**Bifurcation diagram of the logistic map**, with the period-doubling onsets
|
|
67
|
+
marked — `orbit_diagram` is one call, and `.bifurcation_points()` finds the
|
|
68
|
+
cascade ($r_1 = 3$, $r_2 = 1 + \sqrt6 \approx 3.449$, …):
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
import numpy as np, tsdynamics as ts
|
|
72
|
+
from tsdynamics.viz import Annotation
|
|
73
|
+
|
|
74
|
+
orbit = ts.orbit_diagram(ts.Logistic(), "r", np.linspace(2.8, 4.0, 2000))
|
|
75
|
+
pts = orbit.bifurcation_points()
|
|
76
|
+
|
|
77
|
+
spec = orbit.to_plot_spec().relabel(x="r", y="x*", title="Logistic bifurcation")
|
|
78
|
+
spec.style(color="k", s=0.2, alpha=0.5) # tiny semi-transparent dots
|
|
79
|
+
spec.annotations = [
|
|
80
|
+
Annotation("vline", x=pts[i], text=lbl, style={"color": "red", "linestyle": "--"})
|
|
81
|
+
for i, lbl in [(0, " r₁"), (1, " r₂"), (3, " r₄")]
|
|
82
|
+
]
|
|
83
|
+
spec.save("bifurcation.png", size=(1600, 700))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+

|
|
87
|
+
|
|
88
|
+
**A PDE, too** — the Kuramoto–Sivashinsky equation is a built-in spatially
|
|
89
|
+
extended system; its space–time field is auto-detected and drawn as a heatmap:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
ks = ts.KuramotoSivashinsky(N=128, L=22.0)
|
|
93
|
+
traj = ks.integrate(final_time=200.0, dt=0.25)
|
|
94
|
+
traj.to_plot_spec().save("ks.png") # 128-mode space–time field
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
<p align="center">
|
|
100
|
+
<img src="docs/assets/readme/ks_spacetime.png" alt="Kuramoto–Sivashinsky space–time field">
|
|
101
|
+
</p>
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
The spinning attractor at the top is the same `to_plot_spec`, animated:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
traj = ts.Lorenz().integrate(final_time=100.0, dt=0.01)
|
|
108
|
+
spec = traj.to_plot_spec()
|
|
109
|
+
spec.style(axes=False).trail(None).camera(spin=0.4) # full curve, no axes, rotate
|
|
110
|
+
spec.animate(fps=30, duration=10, loop=True)
|
|
111
|
+
spec.save("lorenz.gif")
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## A taste of the analysis layer
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import numpy as np, tsdynamics as ts
|
|
118
|
+
|
|
119
|
+
# Poincaré section of the Rössler attractor (root-refined crossings)
|
|
120
|
+
section = ts.poincare_section(ts.Rossler(), plane=("y", 0.0, "up"), n=500)
|
|
121
|
+
|
|
122
|
+
# Fixed points of the Hénon map, with stability
|
|
123
|
+
ts.fixed_points(ts.Henon())
|
|
124
|
+
# [FixedPoint([-1.1314 -0.3394], unstable), FixedPoint([0.6314 0.1894], unstable)]
|
|
125
|
+
|
|
126
|
+
# Maximal Lyapunov exponent — no Jacobian needed
|
|
127
|
+
ts.max_lyapunov(ts.Lorenz(ic=[1, 1, 1]), dt=0.05) # ≈ 0.9
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Plus: **attractors & basins** of any flow or map, correlation/Rényi **fractal
|
|
131
|
+
dimensions**, **permutation/sample/dispersion entropy**, **RQA** (recurrence
|
|
132
|
+
quantification), **surrogate** hypothesis tests, **delay embedding** (Takens,
|
|
133
|
+
optimal τ, Cao/FNN), GALI & the 0–1 chaos test, and Lyapunov exponents **from a
|
|
134
|
+
bare time series** (Kantz/Rosenstein).
|
|
135
|
+
|
|
136
|
+
## Highlights
|
|
137
|
+
|
|
138
|
+
- **Three families, one interface**
|
|
139
|
+
|
|
140
|
+
- **ODEs**
|
|
141
|
+
|
|
142
|
+
- **DDEs**
|
|
143
|
+
- **SDEs**
|
|
144
|
+
- **Discrete Maps**
|
|
145
|
+
|
|
146
|
+
- **151 built-in systems** with literature parameters.
|
|
147
|
+
|
|
148
|
+
- **Native engine**: equations lower to a Rust engine (an SSA-tape
|
|
149
|
+
interpreter, with a Cranelift JIT alongside) in-process; parameters are runtime
|
|
150
|
+
values, so changing them is free and there is no compile step or cache.
|
|
151
|
+
|
|
152
|
+
- **Composition** — a `PoincareMap` of a flow *is* a discrete map, so
|
|
153
|
+
`orbit_diagram(PoincareMap(Rossler(), ("y", 0.0)), "c", values)` draws the
|
|
154
|
+
bifurcation diagram of a *flow* in one line.
|
|
155
|
+
|
|
156
|
+
- **Backend-neutral plotting** — one `PlotSpec` IR renders to matplotlib, plotly
|
|
157
|
+
(interactive + animated HTML), three.js, or JSON, with a fluent styling/theming
|
|
158
|
+
vocabulary.
|
|
159
|
+
|
|
160
|
+
## Install
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install tsdynamics # or: uv add tsdynamics
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
A prebuilt `abi3` wheel (manylinux / musllinux / macOS / Windows) bundles the
|
|
167
|
+
native Rust engine. No Rust toolchain and no C compiler needed to install or
|
|
168
|
+
run. Optional plotting extra: `tsdynamics[plot,interactive]` (matplotlib, plotly).
|
|
169
|
+
|
|
170
|
+
## Development
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
git clone https://github.com/El3ssar/TSDynamics && cd TSDynamics
|
|
174
|
+
uv sync --group dev --group docs
|
|
175
|
+
uv run pytest -m "not slow" --no-cov # fast tier
|
|
176
|
+
uv run pytest --no-cov # full local suite
|
|
177
|
+
TSD_DOCS_FIGURES=0 uv run mkdocs serve # docs preview
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Releases are automated: conventional-commit PR titles drive
|
|
181
|
+
[semantic-release](https://python-semantic-release.readthedocs.io/) on merge see [CONTRIBUTING](https://el3ssar.github.io/TSDynamics/project/contributing/).
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
MIT © Daniel Estevez
|
|
@@ -321,8 +321,11 @@ pub(super) fn guard_continuous(tape: &Tape) -> Result<(), EngineError> {
|
|
|
321
321
|
|
|
322
322
|
/// Reject a method that needs an analytic Jacobian when the tape carries none.
|
|
323
323
|
///
|
|
324
|
-
/// The
|
|
325
|
-
///
|
|
324
|
+
/// The guard fires for any kernel whose `caps.needs_jacobian` flag is set — i.e.
|
|
325
|
+
/// the whole implicit family (`rosenbrock`/`trbdf2`/`bdf`/`backward_euler`/
|
|
326
|
+
/// `implicit_midpoint`/`trapezoid`/`sdirk2`), not just the two named below.
|
|
327
|
+
/// These freeze `∂f/∂u` each step and solve `(I − c·h·J)`. Driving them over a
|
|
328
|
+
/// tape compiled *without* a Jacobian
|
|
326
329
|
/// (`with_jacobian=False`) leaves `eval_jac`'s `jac` buffer untouched — i.e. the
|
|
327
330
|
/// all-zeros it was initialised to — so the iteration matrix collapses to `I` and
|
|
328
331
|
/// the L-stable step silently degrades to an *unstable forward-Euler* one: a
|
|
@@ -5,13 +5,21 @@
|
|
|
5
5
|
//! Python `_AttractorMapper` (stepping + cell-binning + the shared-label early-out)
|
|
6
6
|
//! inside one engine call, so a `basins_of_attraction` image is computed without
|
|
7
7
|
//! the ~10^6–10^7 per-`dt` Python→FFI round-trips and the per-step Python FSM
|
|
8
|
-
//! overhead the released loop pays. It
|
|
9
|
-
//!
|
|
10
|
-
//!
|
|
11
|
-
//! the
|
|
12
|
-
//! `system.step(dt)` runs) or iterates a map once
|
|
8
|
+
//! overhead the released loop pays. It drives the *same* sequential algorithm
|
|
9
|
+
//! over the *same* engine stepper: each cell-check advances a flow by one `dt` (an
|
|
10
|
+
//! [`integrate_grid`] over the two-node grid `[t, t+dt]`, byte-for-byte the
|
|
11
|
+
//! per-`dt` integration the Python `system.step(dt)` runs) or iterates a map once
|
|
13
12
|
//! ([`map_step_once`](crate::map::map_step_once)).
|
|
14
13
|
//!
|
|
14
|
+
//! For **flows** this is **bit-identical** to the Python mapper: both advance the
|
|
15
|
+
//! same engine stepper, so the label image and the located attractor set match
|
|
16
|
+
//! byte-for-byte. For **maps** the Python mapper iterates the pure-Python `_step`
|
|
17
|
+
//! while this kernel runs the lowered IR; the two differ by ULPs, so a
|
|
18
|
+
//! boundary-straddling iterate can bin into a neighbouring cell. In practice the
|
|
19
|
+
//! map label image is still **empirically byte-identical** across the catalogue,
|
|
20
|
+
//! but the guarantee is only *same-attractor* — a chaotic map's located cells may
|
|
21
|
+
//! sit a few cells apart.
|
|
22
|
+
//!
|
|
15
23
|
//! # Layering
|
|
16
24
|
//!
|
|
17
25
|
//! The kernel is agnostic about *which* evaluator (interpreter vs JIT) and *which*
|
|
@@ -123,10 +123,14 @@ where
|
|
|
123
123
|
// Preallocate the contiguous output and write each trajectory DIRECTLY into
|
|
124
124
|
// its own row slice in parallel. Zipping `states.par_chunks_mut(dim)` with
|
|
125
125
|
// `status.par_iter_mut()` partitions the buffers into disjoint, index-aligned
|
|
126
|
-
// pieces, so worker `i` only ever touches row `i
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
//
|
|
126
|
+
// pieces, so worker `i` only ever touches row `i`. The win is avoiding a
|
|
127
|
+
// GLOBAL collect-then-scatter: there is no intermediate `Vec<Vec<f64>>` of
|
|
128
|
+
// per-trajectory heap allocations to gather and copy back into the contiguous
|
|
129
|
+
// buffer. Each trajectory still copies its own final state once — `integrate_final`
|
|
130
|
+
// returns an owned final-state `Vec`, which is copied into its row. The
|
|
131
|
+
// partition is positional, so the output is still in input order regardless of
|
|
132
|
+
// how rayon schedules the workers (parallel == serial bit-for-bit, the
|
|
133
|
+
// determinism contract above).
|
|
130
134
|
let mut states = vec![0.0; n_ic * dim];
|
|
131
135
|
let mut status = vec![TrajStatus::Ok; n_ic];
|
|
132
136
|
states
|
|
@@ -12,10 +12,14 @@
|
|
|
12
12
|
//! | Kind | Reads | Ops |
|
|
13
13
|
//! |-------------|-----------------------------------------|-----|
|
|
14
14
|
//! | [`Leaf`] | an input (`u`/`p`/`t`) or an immediate | `Const`, `State`, `Param`, `Time` |
|
|
15
|
-
//! | [`Unary`] | register `a` | `Neg`, `Recip`,
|
|
16
|
-
//! | [`Binary`] | registers `a` **and** `b` | `Add`, `Sub`, `Mul`, `Div`, `Pow` |
|
|
15
|
+
//! | [`Unary`] | register `a` | `Neg`, `Recip`, the transcendental functions, and the E-OPS unaries `Floor`, `Ceil` |
|
|
16
|
+
//! | [`Binary`] | registers `a` **and** `b` | `Add`, `Sub`, `Mul`, `Div`, `Pow`, and the E-OPS binaries: comparisons `Lt`, `Le`, `Gt`, `Ge`, `Eq`, `Ne`, plus `Min`, `Max`, `Mod`, `Rem` |
|
|
17
17
|
//! | [`Powi`] | register `a`, integer exponent in `b` | `Powi` |
|
|
18
18
|
//!
|
|
19
|
+
//! (The E-OPS block — the non-smooth / piecewise comparisons, `Min`/`Max`,
|
|
20
|
+
//! `Floor`/`Ceil`, and the floored `Mod` / truncated `Rem` — is the additive
|
|
21
|
+
//! extension of the frozen IR; see the variant list and [`kind()`](Op::kind).)
|
|
22
|
+
//!
|
|
19
23
|
//! For the per-instruction operand layout (which of `a`/`b`/`imm` each kind
|
|
20
24
|
//! reads) see [`crate::Tape`].
|
|
21
25
|
//!
|
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
//! it does not use [`adaptive_step`](super::control::adaptive_step): it computes
|
|
12
12
|
//! the 12 stages with the shared machinery, then forms its own blended error
|
|
13
13
|
//! norm `|h|·‖e₅‖² / √((‖e₅‖² + 0.01·‖e₃‖²)·n)` — identical to SciPy's, where
|
|
14
|
-
//! `‖·‖` is the per-component-scaled Euclidean norm.
|
|
15
|
-
//!
|
|
16
|
-
//!
|
|
14
|
+
//! `‖·‖` is the per-component-scaled Euclidean norm.
|
|
15
|
+
//!
|
|
16
|
+
//! There is **no propagation FSAL reuse** here: every accepted step recomputes
|
|
17
|
+
//! its 12 propagation stages from scratch (unlike `rk45`/`tsit5`/`bs3`, which
|
|
18
|
+
//! reuse the accepted step's last stage as the next step's first). The 13th
|
|
19
|
+
//! "FSAL" stage that SciPy carries is purely a *dense-output* stage — it has zero
|
|
20
|
+
//! weight in both error estimators and feeds no subsequent propagation step — so
|
|
21
|
+
//! it is not computed here; only the 12 propagation stages are needed.
|
|
17
22
|
|
|
18
23
|
// The Hairer coefficients are transcribed verbatim from the canonical published
|
|
19
24
|
// set (also SciPy's `dop853_coefficients.py`), at higher precision than f64 can
|
|
@@ -131,3 +131,61 @@ macro_rules! register_solver {
|
|
|
131
131
|
}
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
|
+
|
|
135
|
+
#[cfg(test)]
|
|
136
|
+
mod tests {
|
|
137
|
+
use super::*;
|
|
138
|
+
|
|
139
|
+
/// Single-source-of-truth guard: every registered kernel's *declared* caps
|
|
140
|
+
/// (the [`register_solver!`] literal stored in [`SolverRegistration::caps`])
|
|
141
|
+
/// must equal the caps its freshly-built instance reports from
|
|
142
|
+
/// [`Solver::caps`](crate::Solver::caps), and likewise for the name.
|
|
143
|
+
///
|
|
144
|
+
/// The two copies are read by *different* engine paths — the Jacobian /
|
|
145
|
+
/// implicit-kind guards read the registered `reg.caps`
|
|
146
|
+
/// (`bridge/marshal.rs`, `bridge/dde.rs`, `bridge/sde.rs`), while the
|
|
147
|
+
/// dense-output / event path reads the instance method `solver.caps()`
|
|
148
|
+
/// (`tsdyn-engine/src/event.rs`). If a kernel author edited one and not the
|
|
149
|
+
/// other (e.g. added `.with_dense()` to the registration but not to
|
|
150
|
+
/// `caps()`), the engine would build a Jacobian-free tape off one capability
|
|
151
|
+
/// set yet drive event refinement off a *different* one — a silent
|
|
152
|
+
/// correctness divergence. This test sees every kernel linked into the test
|
|
153
|
+
/// binary, so any drift fails CI loudly instead.
|
|
154
|
+
#[test]
|
|
155
|
+
fn registered_caps_match_instance_caps() {
|
|
156
|
+
let mut mismatches: Vec<String> = Vec::new();
|
|
157
|
+
for reg in registered() {
|
|
158
|
+
let instance = (reg.make)();
|
|
159
|
+
let inst_caps = instance.caps();
|
|
160
|
+
let inst_name = instance.name();
|
|
161
|
+
if inst_caps != reg.caps {
|
|
162
|
+
mismatches.push(format!(
|
|
163
|
+
"{}: registration caps {:?} != instance caps() {:?}",
|
|
164
|
+
reg.name, reg.caps, inst_caps
|
|
165
|
+
));
|
|
166
|
+
}
|
|
167
|
+
if inst_name != reg.name {
|
|
168
|
+
mismatches.push(format!(
|
|
169
|
+
"registration name {:?} != instance name() {:?}",
|
|
170
|
+
reg.name, inst_name
|
|
171
|
+
));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
assert!(
|
|
175
|
+
mismatches.is_empty(),
|
|
176
|
+
"solver registration/instance capability drift:\n{}",
|
|
177
|
+
mismatches.join("\n")
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/// Sanity: the guard above is non-vacuous — there *are* registered kernels
|
|
182
|
+
/// in the test binary (a dead-stripped registry would make the guard pass
|
|
183
|
+
/// trivially, see the module-level dead-stripping note).
|
|
184
|
+
#[test]
|
|
185
|
+
fn registry_is_non_empty() {
|
|
186
|
+
assert!(
|
|
187
|
+
registered().next().is_some(),
|
|
188
|
+
"no solver kernels registered in the test binary"
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -115,6 +115,15 @@ pub trait Solver: Send {
|
|
|
115
115
|
|
|
116
116
|
/// This kernel's [capabilities](Caps): explicit/implicit, adaptive,
|
|
117
117
|
/// Jacobian need, and supported problem families.
|
|
118
|
+
///
|
|
119
|
+
/// **Must equal the caps the kernel declares in its
|
|
120
|
+
/// [`register_solver!`](crate::register_solver) line**
|
|
121
|
+
/// ([`SolverRegistration::caps`](crate::SolverRegistration)). The two copies
|
|
122
|
+
/// are read by different engine paths — the Jacobian / implicit-kind guards
|
|
123
|
+
/// read the registered copy, while the dense-output / event path reads this
|
|
124
|
+
/// method — so a drift between them is a silent correctness divergence. The
|
|
125
|
+
/// `registered_caps_match_instance_caps` registry test asserts they agree
|
|
126
|
+
/// for every linked kernel, so an out-of-sync edit fails CI.
|
|
118
127
|
fn caps(&self) -> Caps;
|
|
119
128
|
|
|
120
129
|
/// Advance `st` by one step of size `h`, using `ev` for RHS/Jacobian values.
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tsdynamics"
|
|
7
|
-
version = "5.2.
|
|
7
|
+
version = "5.2.6"
|
|
8
8
|
description = "Zero-warmup Rust-engine ODE/DDE/SDE integration, discrete maps, and chaos analysis for dynamical systems."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12,<4.0"
|
|
@@ -152,6 +152,9 @@ ignore = [
|
|
|
152
152
|
"D101", # missing docstring in public class — class names are the documentation
|
|
153
153
|
"F841", # local variable assigned but never used — intentional in _jac methods for visual symmetry
|
|
154
154
|
]
|
|
155
|
+
# Cross-library benchmark harness (auxiliary, not shipped): adapter classes and
|
|
156
|
+
# their task_* methods are self-documenting by name.
|
|
157
|
+
"benchmarks/**" = ["D101", "D102", "D103"]
|
|
155
158
|
|
|
156
159
|
[tool.ruff.lint.isort]
|
|
157
160
|
known-first-party = ["tsdynamics"]
|
|
@@ -363,7 +363,7 @@ bifurcation_diagram = orbit_diagram #: discoverable spelling of :func:`orbit_di
|
|
|
363
363
|
basins = basins_of_attraction #: short alias of :func:`basins_of_attraction`
|
|
364
364
|
|
|
365
365
|
# Single source of truth for the package version; rewritten by python-semantic-release.
|
|
366
|
-
__version__ = "5.2.
|
|
366
|
+
__version__ = "5.2.6"
|
|
367
367
|
|
|
368
368
|
# Built-in system classes are NOT bound into this namespace — that would bury the
|
|
369
369
|
# submodules (``analysis``, ``data``, ``systems``, …) under ~150 model names in
|