tsdynamics 5.2.2__tar.gz → 5.2.3__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.2 → tsdynamics-5.2.3}/PKG-INFO +1 -1
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/pyproject.toml +1 -1
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/__init__.py +7 -6
- tsdynamics-5.2.3/src/tsdynamics/analysis/_plotbuilder.py +434 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_array.py +32 -44
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_base.py +19 -25
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_collection.py +15 -21
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_scalar.py +18 -21
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_scaling.py +12 -35
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/attractors.py +13 -11
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/basins.py +31 -25
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/continuation.py +19 -25
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/metrics.py +10 -17
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/gali.py +9 -13
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/zero_one.py +8 -9
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/_common.py +11 -24
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/generalized.py +10 -12
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/delay.py +9 -12
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/dimension.py +16 -36
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/embed.py +20 -31
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/core.py +9 -9
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/multiscale.py +8 -13
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/_common.py +29 -4
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/fixed.py +49 -63
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/periodic.py +55 -58
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/lyapunov/__init__.py +8 -11
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/lyapunov/from_data.py +14 -19
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/orbit_diagram.py +11 -12
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/return_map.py +22 -35
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/matrix.py +12 -14
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/rqa.py +10 -8
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/windowed.py +7 -8
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/generators.py +11 -20
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/hypothesis.py +12 -18
- tsdynamics-5.2.3/src/tsdynamics/engine/_families.py +414 -0
- tsdynamics-5.2.3/src/tsdynamics/engine/run.py +667 -0
- tsdynamics-5.2.3/src/tsdynamics/engine/run_methods.py +143 -0
- tsdynamics-5.2.3/src/tsdynamics/engine/sde_run.py +143 -0
- tsdynamics-5.2.3/src/tsdynamics/engine/stepper.py +162 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/errors.py +29 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/registry.py +58 -14
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/chaotic_attractors.py +655 -1
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/chem_bio_systems.py +837 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/climate_geophysics.py +428 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/coupled_systems.py +667 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/delayed_systems.py +172 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/exotic_systems.py +554 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/oscillatory_systems.py +354 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/physical_systems.py +137 -1
- tsdynamics-5.2.3/src/tsdynamics/systems/continuous/population_dynamics.py +161 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/spatial_fields.py +4 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/discrete/chaotic_maps.py +113 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/discrete/exotic_maps.py +322 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/discrete/geometric_maps.py +178 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/discrete/polynomial_maps.py +138 -0
- tsdynamics-5.2.3/src/tsdynamics/systems/discrete/population_maps.py +117 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/utils/grids.py +17 -0
- tsdynamics-5.2.2/src/tsdynamics/engine/run.py +0 -1376
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/chem_bio_systems.py +0 -403
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/climate_geophysics.py +0 -217
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/coupled_systems.py +0 -325
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/delayed_systems.py +0 -83
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/exotic_systems.py +0 -234
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/oscillatory_systems.py +0 -184
- tsdynamics-5.2.2/src/tsdynamics/systems/continuous/population_dynamics.py +0 -81
- tsdynamics-5.2.2/src/tsdynamics/systems/discrete/exotic_maps.py +0 -180
- tsdynamics-5.2.2/src/tsdynamics/systems/discrete/geometric_maps.py +0 -102
- tsdynamics-5.2.2/src/tsdynamics/systems/discrete/polynomial_maps.py +0 -62
- tsdynamics-5.2.2/src/tsdynamics/systems/discrete/population_maps.py +0 -62
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/LICENSE +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/README.md +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/Cargo.lock +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/dde.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/events.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/map.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/marshal.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/mod.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/ode.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/sde.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/stepper.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/dde.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/ensemble.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/event.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/integrate.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/map.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/problem.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/rng.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/sde.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/testkit.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/builder.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/eval.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/op.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/reference.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/tape.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/coverage_tape.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/CircadianRhythm.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/Lorenz.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/SanUmSrisuchinwong.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/SprottMore.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/Torus.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/YuWang2.txt +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/gen_fixtures.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/roundtrip.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/codegen.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/error.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/evaluator.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/shims.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/compile_latency.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/interpreter_equivalence.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/caps.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/adams.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/bs3.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/cashkarp.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/control.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/dop853.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/euler.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/heun.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/heun_euler.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/midpoint.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/mod.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/ralston.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk4.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk45.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk4_38.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rkf45.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/ssprk3.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/testkit.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/tsit5.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/backward_euler.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/bdf.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/control.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/implicit_midpoint.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/linalg.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/mod.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/newton.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/rosenbrock.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/sdirk2.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/trapezoid.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/trbdf2.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/registry.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/euler_maruyama.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/milstein.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/mod.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/testkit.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/solver.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/explicit_accuracy.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/implicit_stiff.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/registry.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/Cargo.toml +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/src/interp.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/src/lib.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/tests/golden_fixtures.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/tests/oracle_equivalence.rs +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/_result_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_json.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_viz.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_tangent.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/expansion.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/_scaling.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/correlation.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/fixedmass.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/dispersion.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/lz.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/permutation.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/sample.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/poincare.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/sampling/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/sampling/sagitta.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/statistics.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/sampling.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/trajectory.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_base.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_crossings.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_variational.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/ensemble.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/poincare.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/projected.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/stroboscopic.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/tangent.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/wrapped.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/compile.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/events.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/problem.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/reference.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/symbols.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_accessors.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_dde_lyapunov.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_plottable.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/base.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/continuous.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/delay.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/discrete.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/protocol.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/stochastic.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/wrapped.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/plugins.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/py.typed +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/explicit.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/implicit.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/select.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/stochastic.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/discrete/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/_common.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/_result.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/features.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/preprocessing.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/spectral.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/utils/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/compose.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/export.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/producers.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/caps.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/json.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_anim.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_core.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_threed.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_anim.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_composite.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_core.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_html.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_threed.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/threejs/__init__.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/threejs/_lower.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/spec.py +0 -0
- {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/style.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tsdynamics"
|
|
7
|
-
version = "5.2.
|
|
7
|
+
version = "5.2.3"
|
|
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"
|
|
@@ -46,9 +46,10 @@ Reachable submodules (bound on the top-level namespace, so they show up in
|
|
|
46
46
|
capability.
|
|
47
47
|
- :mod:`~tsdynamics.errors` — the :class:`~tsdynamics.errors.TSDynamicsError`
|
|
48
48
|
hierarchy that public entry points raise.
|
|
49
|
-
- :mod:`~tsdynamics.viz` — the backend-agnostic ``PlotSpec`` IR
|
|
50
|
-
|
|
51
|
-
``import tsdynamics`` pulls in no
|
|
49
|
+
- :mod:`~tsdynamics.viz` — the backend-agnostic ``PlotSpec`` IR plus its
|
|
50
|
+
self-registering renderers (matplotlib / Plotly / JSON / three.js). Resolved
|
|
51
|
+
**lazily** (via ``__getattr__``) so a plain ``import tsdynamics`` pulls in no
|
|
52
|
+
plotting machinery — ``ts.viz`` imports it (and caches it) on first access.
|
|
52
53
|
- :mod:`~tsdynamics.engine`, :mod:`~tsdynamics.solvers` — advanced/internal: the
|
|
53
54
|
Rust-facing compile/run seam and the solver registry. Reachable for inspection
|
|
54
55
|
but rarely imported directly.
|
|
@@ -362,10 +363,10 @@ bifurcation_diagram = orbit_diagram #: discoverable spelling of :func:`orbit_di
|
|
|
362
363
|
basins = basins_of_attraction #: short alias of :func:`basins_of_attraction`
|
|
363
364
|
|
|
364
365
|
# Single source of truth for the package version; rewritten by python-semantic-release.
|
|
365
|
-
__version__ = "5.2.
|
|
366
|
+
__version__ = "5.2.3"
|
|
366
367
|
|
|
367
368
|
# Built-in system classes are NOT bound into this namespace — that would bury the
|
|
368
|
-
# submodules (``analysis``, ``data``, ``systems``, …) under ~
|
|
369
|
+
# submodules (``analysis``, ``data``, ``systems``, …) under ~150 model names in
|
|
369
370
|
# ``dir()`` / autocomplete. The canonical path is ``tsdynamics.systems.<Name>``
|
|
370
371
|
# (e.g. ``tsdynamics.systems.Lorenz``). For backwards compatibility, ``tsd.Lorenz``
|
|
371
372
|
# and ``from tsdynamics import Lorenz`` still resolve, lazily, via ``__getattr__``
|
|
@@ -429,7 +430,7 @@ def __getattr__(name: str) -> Any:
|
|
|
429
430
|
* ``tsdynamics.viz`` — imported on first access (and cached) so a plain
|
|
430
431
|
``import tsdynamics`` pulls in no plotting/IR machinery; ``viz`` still shows
|
|
431
432
|
in ``__all__`` / ``dir()`` for discoverability.
|
|
432
|
-
* ``tsdynamics.Lorenz`` and friends — the ~
|
|
433
|
+
* ``tsdynamics.Lorenz`` and friends — the ~150 built-in system classes,
|
|
433
434
|
resolved from :mod:`tsdynamics.systems` (the canonical path) instead of
|
|
434
435
|
binding all of them into the namespace, which keeps ``dir()`` / autocomplete
|
|
435
436
|
focused on the curated public API.
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
"""Analysis-facing :class:`~tsdynamics.viz.spec.PlotSpec` builder (the result-side seam).
|
|
2
|
+
|
|
3
|
+
The trajectory side funnels every plot through the parameterised
|
|
4
|
+
:mod:`tsdynamics.viz.producers`; the **analysis** side — the ~two-dozen
|
|
5
|
+
:class:`~tsdynamics.analysis._result.AnalysisResult` ``to_plot_spec`` methods —
|
|
6
|
+
used to each hand-assemble the same ``Layer(...)`` / ``Axis(...)`` /
|
|
7
|
+
``PlotSpec(...)`` boilerplate inline. This module is the analysis counterpart of
|
|
8
|
+
the producers: a handful of small, engine-free, backend-free helpers that
|
|
9
|
+
encapsulate the recurring build-layers / set-axes / wrap-and-copy-meta patterns,
|
|
10
|
+
so a result class describes *what* it draws (a scaling fit, a diagnostic curve, a
|
|
11
|
+
sparse recurrence scatter, a bar readout, an image) and the IR vocabulary lives
|
|
12
|
+
**here**, in one place, instead of being threaded through every result module.
|
|
13
|
+
|
|
14
|
+
Like the producers it imports the :mod:`tsdynamics.viz.spec` IR **lazily** (every
|
|
15
|
+
function-body import is deferred), so building a result — or merely importing
|
|
16
|
+
:mod:`tsdynamics` — never pulls a plotting library.
|
|
17
|
+
|
|
18
|
+
The catalogue
|
|
19
|
+
-------------
|
|
20
|
+
Two layers of helper:
|
|
21
|
+
|
|
22
|
+
- **Layer / annotation constructors** — :func:`line`, :func:`line3d`,
|
|
23
|
+
:func:`scatter`, :func:`markers`, :func:`bar`, :func:`image`, :func:`area`,
|
|
24
|
+
:func:`errorbar`, :func:`histogram` (one per low-level mark a result draws) and
|
|
25
|
+
:func:`vline` / :func:`hline` / :func:`span` / :func:`text` (the reference
|
|
26
|
+
overlays). A result builds its drawables with these and never names a raw
|
|
27
|
+
:class:`~tsdynamics.viz.spec.PlotKind` mark.
|
|
28
|
+
- **Spec assemblers** — :func:`spec` (the general "resolve the kind, set the
|
|
29
|
+
labelled axes, attach legend / colorbar / annotations / meta" wrapper every
|
|
30
|
+
bespoke site shares) plus the named-pattern shortcuts :func:`scaling_fit` (the
|
|
31
|
+
log--log SCATTER + fit-region MARKERS + fit LINE schema the dimension /
|
|
32
|
+
Lyapunov-from-data estimators emit) and :func:`diagonal` (the ``y = x``
|
|
33
|
+
reference line a return map / cobweb draws under its scatter).
|
|
34
|
+
|
|
35
|
+
Every produced spec uses only the closed
|
|
36
|
+
:class:`~tsdynamics.viz.spec.Layer` channel vocabulary and the frozen
|
|
37
|
+
:class:`~tsdynamics.viz.spec.PlotKind` enum, so it round-trips losslessly through
|
|
38
|
+
:meth:`~tsdynamics.viz.spec.PlotSpec.to_dict` /
|
|
39
|
+
:meth:`~tsdynamics.viz.spec.PlotSpec.from_dict` exactly as the hand-built specs
|
|
40
|
+
did — this is a *refactor*, output-equivalent by construction.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
from collections.abc import Mapping, Sequence
|
|
46
|
+
from typing import TYPE_CHECKING, Any
|
|
47
|
+
|
|
48
|
+
import numpy as np
|
|
49
|
+
|
|
50
|
+
if TYPE_CHECKING:
|
|
51
|
+
from tsdynamics.viz.spec import Annotation, Layer, PlotKind, PlotSpec
|
|
52
|
+
|
|
53
|
+
__all__ = [
|
|
54
|
+
"area",
|
|
55
|
+
"bar",
|
|
56
|
+
"diagonal",
|
|
57
|
+
"errorbar",
|
|
58
|
+
"histogram",
|
|
59
|
+
"hline",
|
|
60
|
+
"image",
|
|
61
|
+
"line",
|
|
62
|
+
"line3d",
|
|
63
|
+
"markers",
|
|
64
|
+
"scaling_fit",
|
|
65
|
+
"scatter",
|
|
66
|
+
"spec",
|
|
67
|
+
"text",
|
|
68
|
+
"vline",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Layer constructors (one per low-level mark a result draws)
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _layer(
|
|
78
|
+
mark: str,
|
|
79
|
+
data: Mapping[str, Any],
|
|
80
|
+
*,
|
|
81
|
+
label: str | None = None,
|
|
82
|
+
style: Mapping[str, Any] | None = None,
|
|
83
|
+
) -> Layer:
|
|
84
|
+
"""Build one :class:`~tsdynamics.viz.spec.Layer` (the lazy-import choke point)."""
|
|
85
|
+
from tsdynamics.viz.spec import Layer, PlotKind
|
|
86
|
+
|
|
87
|
+
return Layer(
|
|
88
|
+
PlotKind(mark),
|
|
89
|
+
dict(data),
|
|
90
|
+
label=label,
|
|
91
|
+
style=dict(style) if style else {},
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def line(
|
|
96
|
+
x: Any,
|
|
97
|
+
y: Any,
|
|
98
|
+
*,
|
|
99
|
+
label: str | None = None,
|
|
100
|
+
style: Mapping[str, Any] | None = None,
|
|
101
|
+
) -> Layer:
|
|
102
|
+
"""Build a 2-D ``LINE`` layer over ``(x, y)``."""
|
|
103
|
+
return _layer("line", {"x": x, "y": y}, label=label, style=style)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def line3d(
|
|
107
|
+
x: Any,
|
|
108
|
+
y: Any,
|
|
109
|
+
z: Any,
|
|
110
|
+
*,
|
|
111
|
+
label: str | None = None,
|
|
112
|
+
style: Mapping[str, Any] | None = None,
|
|
113
|
+
) -> Layer:
|
|
114
|
+
"""Build a 3-D ``LINE3D`` layer over ``(x, y, z)``."""
|
|
115
|
+
return _layer("line3d", {"x": x, "y": y, "z": z}, label=label, style=style)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def scatter(
|
|
119
|
+
x: Any,
|
|
120
|
+
y: Any,
|
|
121
|
+
*,
|
|
122
|
+
cat: Any = None,
|
|
123
|
+
c: Any = None,
|
|
124
|
+
label: str | None = None,
|
|
125
|
+
style: Mapping[str, Any] | None = None,
|
|
126
|
+
) -> Layer:
|
|
127
|
+
"""Build a 2-D ``SCATTER`` point cloud over ``(x, y)``.
|
|
128
|
+
|
|
129
|
+
``cat`` (per-point category index, paired with a categorical palette) and
|
|
130
|
+
``c`` (per-point scalar colour field) are optional extra channels.
|
|
131
|
+
"""
|
|
132
|
+
data: dict[str, Any] = {"x": x, "y": y}
|
|
133
|
+
if cat is not None:
|
|
134
|
+
data["cat"] = cat
|
|
135
|
+
if c is not None:
|
|
136
|
+
data["c"] = c
|
|
137
|
+
return _layer("scatter", data, label=label, style=style)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def markers(
|
|
141
|
+
x: Any,
|
|
142
|
+
y: Any,
|
|
143
|
+
*,
|
|
144
|
+
z: Any = None,
|
|
145
|
+
label: str | None = None,
|
|
146
|
+
style: Mapping[str, Any] | None = None,
|
|
147
|
+
) -> Layer:
|
|
148
|
+
"""Build a ``MARKERS`` layer over ``(x, y)`` (a few points), optionally 3-D (``z``)."""
|
|
149
|
+
data: dict[str, Any] = {"x": x, "y": y}
|
|
150
|
+
if z is not None:
|
|
151
|
+
data["z"] = z
|
|
152
|
+
return _layer("markers", data, label=label, style=style)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def bar(
|
|
156
|
+
y: Any,
|
|
157
|
+
*,
|
|
158
|
+
x: Any = None,
|
|
159
|
+
cat: Any = None,
|
|
160
|
+
label: str | None = None,
|
|
161
|
+
style: Mapping[str, Any] | None = None,
|
|
162
|
+
) -> Layer:
|
|
163
|
+
"""Build a ``BAR`` layer of heights ``y``, positioned by ``x`` (index) or ``cat`` (category)."""
|
|
164
|
+
data: dict[str, Any] = {"y": y}
|
|
165
|
+
if cat is not None:
|
|
166
|
+
data["cat"] = cat
|
|
167
|
+
if x is not None:
|
|
168
|
+
data["x"] = x
|
|
169
|
+
return _layer("bar", data, label=label, style=style)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def image(
|
|
173
|
+
c: Any,
|
|
174
|
+
*,
|
|
175
|
+
x: Any = None,
|
|
176
|
+
y: Any = None,
|
|
177
|
+
z: Any = None,
|
|
178
|
+
label: str | None = None,
|
|
179
|
+
style: Mapping[str, Any] | None = None,
|
|
180
|
+
) -> Layer:
|
|
181
|
+
"""Build an ``IMAGE`` layer with color field ``c`` (and optional ``x`` / ``y`` / ``z``)."""
|
|
182
|
+
data: dict[str, Any] = {"c": c}
|
|
183
|
+
if x is not None:
|
|
184
|
+
data["x"] = x
|
|
185
|
+
if y is not None:
|
|
186
|
+
data["y"] = y
|
|
187
|
+
if z is not None:
|
|
188
|
+
data["z"] = z
|
|
189
|
+
return _layer("image", data, label=label, style=style)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def area(
|
|
193
|
+
x: Any,
|
|
194
|
+
y: Any,
|
|
195
|
+
*,
|
|
196
|
+
lo: Any,
|
|
197
|
+
hi: Any,
|
|
198
|
+
label: str | None = None,
|
|
199
|
+
style: Mapping[str, Any] | None = None,
|
|
200
|
+
) -> Layer:
|
|
201
|
+
"""Build an ``AREA`` band layer (``lo <= hi`` envelope around ``y`` over ``x``)."""
|
|
202
|
+
return _layer("area", {"x": x, "y": y, "lo": lo, "hi": hi}, label=label, style=style)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def errorbar(
|
|
206
|
+
x: Any,
|
|
207
|
+
y: Any,
|
|
208
|
+
err: Any,
|
|
209
|
+
*,
|
|
210
|
+
label: str | None = None,
|
|
211
|
+
style: Mapping[str, Any] | None = None,
|
|
212
|
+
) -> Layer:
|
|
213
|
+
"""Build an ``ERRORBAR`` layer (symmetric ``err`` magnitudes over ``(x, y)``)."""
|
|
214
|
+
return _layer("errorbar", {"x": x, "y": y, "err": err}, label=label, style=style)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def histogram(
|
|
218
|
+
x: Any,
|
|
219
|
+
*,
|
|
220
|
+
label: str | None = None,
|
|
221
|
+
style: Mapping[str, Any] | None = None,
|
|
222
|
+
) -> Layer:
|
|
223
|
+
"""Build a ``HISTOGRAM`` layer over the sample values ``x``."""
|
|
224
|
+
return _layer("histogram", {"x": x}, label=label, style=style)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# ---------------------------------------------------------------------------
|
|
228
|
+
# Annotation constructors (reference lines / shaded bands / text overlays)
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def vline(x: float, *, text: str = "", style: Mapping[str, Any] | None = None) -> Annotation:
|
|
233
|
+
"""Build a vertical reference line at constant ``x``."""
|
|
234
|
+
from tsdynamics.viz.spec import Annotation
|
|
235
|
+
|
|
236
|
+
return Annotation(kind="vline", x=float(x), text=text, style=dict(style) if style else {})
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def hline(y: float, *, text: str = "", style: Mapping[str, Any] | None = None) -> Annotation:
|
|
240
|
+
"""Build a horizontal reference line at constant ``y``."""
|
|
241
|
+
from tsdynamics.viz.spec import Annotation
|
|
242
|
+
|
|
243
|
+
return Annotation(kind="hline", y=float(y), text=text, style=dict(style) if style else {})
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def span(
|
|
247
|
+
lo: float,
|
|
248
|
+
hi: float,
|
|
249
|
+
*,
|
|
250
|
+
axis: str = "x",
|
|
251
|
+
text: str = "",
|
|
252
|
+
style: Mapping[str, Any] | None = None,
|
|
253
|
+
) -> Annotation:
|
|
254
|
+
"""Build a shaded band between ``lo`` and ``hi`` along ``axis`` (a rejection tail / fit region)."""
|
|
255
|
+
from tsdynamics.viz.spec import Annotation
|
|
256
|
+
|
|
257
|
+
return Annotation(
|
|
258
|
+
kind="span",
|
|
259
|
+
span=(float(lo), float(hi)),
|
|
260
|
+
axis=axis, # type: ignore[arg-type]
|
|
261
|
+
text=text,
|
|
262
|
+
style=dict(style) if style else {},
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def text(x: float, y: float, label: str, *, style: Mapping[str, Any] | None = None) -> Annotation:
|
|
267
|
+
"""Build a text overlay at ``(x, y)``."""
|
|
268
|
+
from tsdynamics.viz.spec import Annotation
|
|
269
|
+
|
|
270
|
+
return Annotation(
|
|
271
|
+
kind="text", x=float(x), y=float(y), text=label, style=dict(style) if style else {}
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ---------------------------------------------------------------------------
|
|
276
|
+
# Spec assemblers
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def spec(
|
|
281
|
+
kind: str | None,
|
|
282
|
+
default: PlotKind | str,
|
|
283
|
+
*,
|
|
284
|
+
layers: Sequence[Layer],
|
|
285
|
+
xlabel: str = "",
|
|
286
|
+
ylabel: str = "",
|
|
287
|
+
zlabel: str | None = None,
|
|
288
|
+
title: str = "",
|
|
289
|
+
ndim: int | None = None,
|
|
290
|
+
aspect: str = "auto",
|
|
291
|
+
xlimits: tuple[float, float] | None = None,
|
|
292
|
+
ylimits: tuple[float, float] | None = None,
|
|
293
|
+
xscale: str = "linear",
|
|
294
|
+
yscale: str = "linear",
|
|
295
|
+
xcategories: Sequence[str] | None = None,
|
|
296
|
+
xticks: Sequence[float] | None = None,
|
|
297
|
+
annotations: Sequence[Annotation] | None = None,
|
|
298
|
+
legend: bool | Any = False,
|
|
299
|
+
colorbar: Any = None,
|
|
300
|
+
clim: tuple[float, float] | None = None,
|
|
301
|
+
meta: Mapping[str, Any] | None = None,
|
|
302
|
+
) -> PlotSpec:
|
|
303
|
+
"""Assemble a finished :class:`~tsdynamics.viz.spec.PlotSpec` from labelled axes + layers.
|
|
304
|
+
|
|
305
|
+
The single "resolve the semantic kind, build the labelled
|
|
306
|
+
:class:`~tsdynamics.viz.spec.Axis` pair (or triple), attach the optional
|
|
307
|
+
legend / colorbar / annotations / clim and copy meta" wrapper every bespoke
|
|
308
|
+
``to_plot_spec`` opens and closes with. ``kind`` is the caller's override (or
|
|
309
|
+
``None``) and ``default`` the result's natural kind — resolved through the
|
|
310
|
+
closed :class:`~tsdynamics.viz.spec.PlotKind` vocabulary exactly as before.
|
|
311
|
+
|
|
312
|
+
``ndim`` defaults to ``3`` when a ``zlabel`` is given, else ``2``. ``legend``
|
|
313
|
+
accepts a ready :class:`~tsdynamics.viz.spec.Legend`, or ``True`` for a default
|
|
314
|
+
one, or ``False`` for none. The ``meta`` mapping is shallow-copied onto the
|
|
315
|
+
spec (provenance is never shared by reference).
|
|
316
|
+
"""
|
|
317
|
+
from tsdynamics.viz.spec import Axis, Legend, PlotKind, PlotSpec
|
|
318
|
+
|
|
319
|
+
spec_kind = PlotKind(kind) if kind is not None else PlotKind(default)
|
|
320
|
+
z_axis = Axis(label=zlabel) if zlabel is not None else None
|
|
321
|
+
resolved_ndim = ndim if ndim is not None else (3 if z_axis is not None else 2)
|
|
322
|
+
|
|
323
|
+
if legend is True:
|
|
324
|
+
legend_obj: Any = Legend()
|
|
325
|
+
elif legend is False:
|
|
326
|
+
legend_obj = None
|
|
327
|
+
else:
|
|
328
|
+
legend_obj = legend
|
|
329
|
+
|
|
330
|
+
return PlotSpec(
|
|
331
|
+
kind=spec_kind,
|
|
332
|
+
ndim=resolved_ndim, # type: ignore[arg-type]
|
|
333
|
+
aspect=aspect, # type: ignore[arg-type]
|
|
334
|
+
title=title,
|
|
335
|
+
x=Axis(
|
|
336
|
+
label=xlabel,
|
|
337
|
+
scale=xscale, # type: ignore[arg-type]
|
|
338
|
+
limits=xlimits,
|
|
339
|
+
categories=xcategories,
|
|
340
|
+
ticks=xticks,
|
|
341
|
+
),
|
|
342
|
+
y=Axis(label=ylabel, scale=yscale, limits=ylimits), # type: ignore[arg-type]
|
|
343
|
+
z=z_axis,
|
|
344
|
+
layers=list(layers),
|
|
345
|
+
legend=legend_obj,
|
|
346
|
+
colorbar=colorbar,
|
|
347
|
+
clim=clim,
|
|
348
|
+
annotations=list(annotations) if annotations else [],
|
|
349
|
+
meta=dict(meta) if meta else {},
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def scaling_fit(
|
|
354
|
+
kind: str | None,
|
|
355
|
+
x: Any,
|
|
356
|
+
y: Any,
|
|
357
|
+
*,
|
|
358
|
+
fit_region: tuple[int, int],
|
|
359
|
+
slope: float,
|
|
360
|
+
line_y: Any | None = None,
|
|
361
|
+
intercept: float | None = None,
|
|
362
|
+
curve_label: str = "curve",
|
|
363
|
+
xlabel: str = "",
|
|
364
|
+
ylabel: str = "",
|
|
365
|
+
title: str = "",
|
|
366
|
+
default: PlotKind | str | None = None,
|
|
367
|
+
meta: Mapping[str, Any] | None = None,
|
|
368
|
+
) -> PlotSpec:
|
|
369
|
+
r"""Build the log--log ``SCALING_FIT`` schema shared by every scaling estimator.
|
|
370
|
+
|
|
371
|
+
The recurring three-layer figure: a ``SCATTER`` of the whole ``(x, y)`` curve,
|
|
372
|
+
a ``MARKERS`` highlight of the fitted scaling region ``x[lo:hi+1]``, and a
|
|
373
|
+
``LINE`` of the fit of the given ``slope`` drawn across the fit region — the
|
|
374
|
+
schema the fractal-dimension estimators, ``LyapunovFromData`` and the
|
|
375
|
+
uncertainty exponent all emit, so a single ``result.plot.scaling()`` renders
|
|
376
|
+
them identically.
|
|
377
|
+
|
|
378
|
+
The fit line is given **either** as a ready ``line_y`` array (already evaluated
|
|
379
|
+
at ``[x[lo], x[hi]]``, e.g. when the line is anchored to a centroid) **or** via
|
|
380
|
+
``intercept`` (then ``y = intercept + slope * [x[lo], x[hi]]``). The
|
|
381
|
+
fit-region layers are added only when the curve is non-empty and the region is
|
|
382
|
+
valid (``hi >= lo``).
|
|
383
|
+
|
|
384
|
+
Parameters
|
|
385
|
+
----------
|
|
386
|
+
kind : str or None
|
|
387
|
+
The caller's semantic-kind override.
|
|
388
|
+
x, y : array-like
|
|
389
|
+
The log--log curve.
|
|
390
|
+
fit_region : tuple of int
|
|
391
|
+
``(lo, hi)`` **inclusive** index bounds of the fitted scaling region.
|
|
392
|
+
slope : float
|
|
393
|
+
The estimated slope (the dimension / exponent) — used in the fit-line
|
|
394
|
+
label ``slope = <slope>``.
|
|
395
|
+
line_y : array-like, optional
|
|
396
|
+
The fit-line ordinates at ``[x[lo], x[hi]]``; mutually exclusive with
|
|
397
|
+
``intercept``.
|
|
398
|
+
intercept : float, optional
|
|
399
|
+
The fit-line intercept (``y = intercept + slope * x``); used when
|
|
400
|
+
``line_y`` is not given.
|
|
401
|
+
"""
|
|
402
|
+
x = np.asarray(x, dtype=float)
|
|
403
|
+
y = np.asarray(y, dtype=float)
|
|
404
|
+
lo, hi = fit_region
|
|
405
|
+
layers: list[Layer] = [scatter(x, y, label=curve_label)]
|
|
406
|
+
if x.size and hi >= lo:
|
|
407
|
+
layers.append(markers(x[lo : hi + 1], y[lo : hi + 1], label="fit region"))
|
|
408
|
+
fit_x = np.array([x[lo], x[hi]], dtype=float)
|
|
409
|
+
if line_y is not None:
|
|
410
|
+
fit_y = np.asarray(line_y, dtype=float)
|
|
411
|
+
else:
|
|
412
|
+
fit_y = float(intercept if intercept is not None else 0.0) + slope * fit_x
|
|
413
|
+
layers.append(line(fit_x, fit_y, label=f"slope = {slope:.3g}"))
|
|
414
|
+
return spec(
|
|
415
|
+
kind,
|
|
416
|
+
default if default is not None else "scaling_fit",
|
|
417
|
+
layers=layers,
|
|
418
|
+
xlabel=xlabel,
|
|
419
|
+
ylabel=ylabel,
|
|
420
|
+
title=title,
|
|
421
|
+
meta=meta,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def diagonal(x: Any, y: Any, *, label: str = "$v_{n+1}=v_n$") -> Layer:
|
|
426
|
+
r"""Build the ``y = x`` reference ``LINE`` spanning the combined range of ``x`` and ``y``.
|
|
427
|
+
|
|
428
|
+
The fixed-point locus a first-return map / cobweb draws under its scatter — a
|
|
429
|
+
line from ``(m, m)`` to ``(M, M)`` where ``[m, M]`` is the range of both
|
|
430
|
+
series combined.
|
|
431
|
+
"""
|
|
432
|
+
both = np.concatenate([np.asarray(x, dtype=float), np.asarray(y, dtype=float)])
|
|
433
|
+
diag = np.array([float(both.min()), float(both.max())])
|
|
434
|
+
return line(diag, diag, label=label)
|
|
@@ -11,7 +11,6 @@ from typing import Any, ClassVar
|
|
|
11
11
|
|
|
12
12
|
import numpy as np
|
|
13
13
|
|
|
14
|
-
from tsdynamics._result_common import resolve_plot_kind
|
|
15
14
|
from tsdynamics.analysis._result_base import AnalysisResult
|
|
16
15
|
from tsdynamics.analysis._result_json import _fmt, _jsonify
|
|
17
16
|
from tsdynamics.analysis._result_viz import VisualizationNotInstalled
|
|
@@ -175,68 +174,57 @@ class ArrayResult(AnalysisResult):
|
|
|
175
174
|
VisualizationNotInstalled
|
|
176
175
|
If the array is empty or higher-than-2-D (nothing generic to draw).
|
|
177
176
|
"""
|
|
178
|
-
from
|
|
177
|
+
from . import _plotbuilder as pb
|
|
179
178
|
|
|
180
179
|
arr = np.asarray(self.values, dtype=float)
|
|
181
180
|
title = type(self).__name__
|
|
182
|
-
meta = dict(self.meta) if self.meta else {}
|
|
183
181
|
|
|
184
182
|
if arr.ndim == 1 and arr.size:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
183
|
+
return pb.spec(
|
|
184
|
+
kind,
|
|
185
|
+
"diagnostic_curve",
|
|
186
|
+
layers=[pb.line(np.arange(arr.size, dtype=float), arr)],
|
|
187
|
+
xlabel="index",
|
|
188
|
+
ylabel="value",
|
|
190
189
|
title=title,
|
|
191
|
-
|
|
192
|
-
y=Axis(label="value"),
|
|
193
|
-
layers=[layer],
|
|
194
|
-
meta=meta,
|
|
190
|
+
meta=self.meta,
|
|
195
191
|
)
|
|
196
192
|
|
|
197
193
|
if arr.ndim == 2 and arr.shape[0] and arr.shape[1] in (2, 3):
|
|
198
194
|
if arr.shape[1] == 3:
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
195
|
+
return pb.spec(
|
|
196
|
+
kind,
|
|
197
|
+
"phase_portrait_3d",
|
|
198
|
+
layers=[pb.line3d(arr[:, 0], arr[:, 1], arr[:, 2])],
|
|
199
|
+
xlabel="x1",
|
|
200
|
+
ylabel="x2",
|
|
201
|
+
zlabel="x3",
|
|
204
202
|
title=title,
|
|
205
|
-
|
|
206
|
-
y=Axis(label="x2"),
|
|
207
|
-
z=Axis(label="x3"),
|
|
208
|
-
layers=[layer],
|
|
209
|
-
meta=meta,
|
|
203
|
+
meta=self.meta,
|
|
210
204
|
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
ndim=2,
|
|
205
|
+
return pb.spec(
|
|
206
|
+
kind,
|
|
207
|
+
"phase_portrait_2d",
|
|
208
|
+
layers=[pb.scatter(arr[:, 0], arr[:, 1])],
|
|
216
209
|
aspect="equal",
|
|
210
|
+
xlabel="x1",
|
|
211
|
+
ylabel="x2",
|
|
217
212
|
title=title,
|
|
218
|
-
|
|
219
|
-
y=Axis(label="x2"),
|
|
220
|
-
layers=[layer],
|
|
221
|
-
meta=meta,
|
|
213
|
+
meta=self.meta,
|
|
222
214
|
)
|
|
223
215
|
|
|
224
216
|
if arr.ndim == 2 and arr.shape[0]:
|
|
225
|
-
spec_kind = resolve_plot_kind(kind, PlotKind.LINE_FAMILY)
|
|
226
217
|
idx = np.arange(arr.shape[0], dtype=float)
|
|
227
|
-
layers = [
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return PlotSpec(
|
|
232
|
-
kind=spec_kind,
|
|
233
|
-
ndim=2,
|
|
234
|
-
title=title,
|
|
235
|
-
x=Axis(label="index"),
|
|
236
|
-
y=Axis(label="value"),
|
|
218
|
+
layers = [pb.line(idx, arr[:, j], label=f"[{j}]") for j in range(min(arr.shape[1], 12))]
|
|
219
|
+
return pb.spec(
|
|
220
|
+
kind,
|
|
221
|
+
"line_family",
|
|
237
222
|
layers=layers,
|
|
238
|
-
|
|
239
|
-
|
|
223
|
+
xlabel="index",
|
|
224
|
+
ylabel="value",
|
|
225
|
+
title=title,
|
|
226
|
+
legend=True,
|
|
227
|
+
meta=self.meta,
|
|
240
228
|
)
|
|
241
229
|
|
|
242
230
|
raise VisualizationNotInstalled(
|
|
@@ -15,7 +15,6 @@ from typing import TYPE_CHECKING, Any, ClassVar
|
|
|
15
15
|
|
|
16
16
|
import numpy as np
|
|
17
17
|
|
|
18
|
-
from tsdynamics._result_common import resolve_plot_kind
|
|
19
18
|
from tsdynamics.analysis._result_json import _fmt, _is_frame_scalar, _jsonify
|
|
20
19
|
from tsdynamics.analysis._result_viz import VisualizationNotInstalled, _PlotAccessor
|
|
21
20
|
|
|
@@ -289,37 +288,32 @@ class AnalysisResult:
|
|
|
289
288
|
bespoke ``to_plot_spec`` is required. Subclasses with such a result
|
|
290
289
|
override this method.
|
|
291
290
|
"""
|
|
292
|
-
from
|
|
291
|
+
from . import _plotbuilder as pb
|
|
293
292
|
|
|
294
|
-
spec_kind = resolve_plot_kind(kind, PlotKind.DIAGNOSTIC_CURVE)
|
|
295
293
|
arrays, scalars = self._plottable_fields()
|
|
296
294
|
|
|
297
|
-
layers
|
|
295
|
+
layers = []
|
|
298
296
|
xlabel, ylabel = "index", "value"
|
|
299
297
|
if len(arrays) >= 2 and arrays[0][1].size == arrays[1][1].size:
|
|
300
298
|
(xname, x), (yname, y) = arrays[0], arrays[1]
|
|
301
|
-
layers.append(
|
|
299
|
+
layers.append(pb.line(x, y, label=yname))
|
|
302
300
|
xlabel, ylabel = xname, yname
|
|
303
301
|
elif arrays:
|
|
304
302
|
yname, y = arrays[0]
|
|
305
|
-
layers.append(
|
|
306
|
-
Layer(PlotKind.LINE, {"x": np.arange(y.size, dtype=float), "y": y}, label=yname)
|
|
307
|
-
)
|
|
303
|
+
layers.append(pb.line(np.arange(y.size, dtype=float), y, label=yname))
|
|
308
304
|
ylabel = yname
|
|
309
305
|
elif scalars:
|
|
310
306
|
names = [n for n, _ in scalars]
|
|
311
307
|
vals = np.array([v for _, v in scalars], dtype=float)
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
308
|
+
return pb.spec(
|
|
309
|
+
kind,
|
|
310
|
+
"diagnostic_curve",
|
|
311
|
+
layers=[pb.markers(np.arange(vals.size, dtype=float), vals)],
|
|
312
|
+
xlabel="field",
|
|
313
|
+
xticks=list(range(len(names))),
|
|
314
|
+
ylabel="value",
|
|
318
315
|
title=type(self).__name__,
|
|
319
|
-
|
|
320
|
-
y=Axis(label="value"),
|
|
321
|
-
layers=layers,
|
|
322
|
-
meta=dict(self.meta) if self.meta else {},
|
|
316
|
+
meta=self.meta,
|
|
323
317
|
)
|
|
324
318
|
else:
|
|
325
319
|
raise VisualizationNotInstalled(
|
|
@@ -328,14 +322,14 @@ class AnalysisResult:
|
|
|
328
322
|
"bespoke to_plot_spec(); export it with .to_dict() meanwhile."
|
|
329
323
|
)
|
|
330
324
|
|
|
331
|
-
return
|
|
332
|
-
kind
|
|
333
|
-
|
|
334
|
-
title=type(self).__name__,
|
|
335
|
-
x=Axis(label=xlabel),
|
|
336
|
-
y=Axis(label=ylabel),
|
|
325
|
+
return pb.spec(
|
|
326
|
+
kind,
|
|
327
|
+
"diagnostic_curve",
|
|
337
328
|
layers=layers,
|
|
338
|
-
|
|
329
|
+
xlabel=xlabel,
|
|
330
|
+
ylabel=ylabel,
|
|
331
|
+
title=type(self).__name__,
|
|
332
|
+
meta=self.meta,
|
|
339
333
|
)
|
|
340
334
|
|
|
341
335
|
def overlay_on(self, base: PlotSpec, *, kind: str | None = None) -> PlotSpec:
|