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.
Files changed (264) hide show
  1. tsdynamics-5.2.6/PKG-INFO +221 -0
  2. tsdynamics-5.2.6/README.md +185 -0
  3. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/marshal.rs +5 -2
  4. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/basin.rs +13 -5
  5. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/ensemble.rs +8 -4
  6. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/op.rs +6 -2
  7. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/dop853.rs +8 -3
  8. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/registry.rs +58 -0
  9. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/solver.rs +9 -0
  10. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/pyproject.toml +4 -1
  11. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/__init__.py +1 -1
  12. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_base.py +1 -0
  13. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_scaling.py +30 -9
  14. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/metrics.py +17 -3
  15. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/expansion.py +1 -1
  16. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/zero_one.py +7 -1
  17. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/_common.py +1 -1
  18. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/correlation.py +12 -1
  19. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/delay.py +23 -8
  20. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/dimension.py +13 -0
  21. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/sample.py +11 -0
  22. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/_interval.py +54 -8
  23. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/periodic.py +9 -1
  24. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/lyapunov/__init__.py +28 -5
  25. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/lyapunov/from_data.py +10 -2
  26. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/orbit_diagram.py +52 -5
  27. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/sampling/sagitta.py +24 -6
  28. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/_common.py +15 -7
  29. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/projected.py +34 -16
  30. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/tangent.py +46 -4
  31. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/events.py +10 -3
  32. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/continuous.py +80 -1
  33. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/discrete.py +36 -2
  34. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/wrapped.py +4 -3
  35. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/plugins.py +4 -3
  36. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/chaotic_attractors.py +6 -4
  37. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/climate_geophysics.py +11 -2
  38. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/exotic_systems.py +6 -0
  39. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/oscillatory_systems.py +18 -4
  40. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/chaotic_maps.py +4 -6
  41. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/exotic_maps.py +14 -8
  42. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/geometric_maps.py +10 -1
  43. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/spectral.py +3 -1
  44. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/compose.py +23 -6
  45. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_core.py +78 -8
  46. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_anim.py +80 -9
  47. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/threejs/_lower.py +7 -5
  48. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/spec.py +8 -2
  49. tsdynamics-5.2.5/PKG-INFO +0 -160
  50. tsdynamics-5.2.5/README.md +0 -124
  51. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/LICENSE +0 -0
  52. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/Cargo.toml +0 -0
  53. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/Cargo.lock +0 -0
  54. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/Cargo.toml +0 -0
  55. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/basin.rs +0 -0
  56. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/dde.rs +0 -0
  57. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/events.rs +0 -0
  58. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/lyapunov.rs +0 -0
  59. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/map.rs +0 -0
  60. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/map_lyapunov.rs +0 -0
  61. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/mod.rs +0 -0
  62. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/ode.rs +0 -0
  63. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/sde.rs +0 -0
  64. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/bridge/stepper.rs +0 -0
  65. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-core/src/lib.rs +0 -0
  66. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/Cargo.toml +0 -0
  67. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/dde.rs +0 -0
  68. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/event.rs +0 -0
  69. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/integrate.rs +0 -0
  70. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/lib.rs +0 -0
  71. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/lyapunov.rs +0 -0
  72. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/map.rs +0 -0
  73. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/map_lyapunov.rs +0 -0
  74. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/param_sweep.rs +0 -0
  75. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/problem.rs +0 -0
  76. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/rng.rs +0 -0
  77. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/sde.rs +0 -0
  78. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-engine/src/testkit.rs +0 -0
  79. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/Cargo.toml +0 -0
  80. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/builder.rs +0 -0
  81. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/eval.rs +0 -0
  82. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/lib.rs +0 -0
  83. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/reference.rs +0 -0
  84. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/src/tape.rs +0 -0
  85. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/coverage_tape.rs +0 -0
  86. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/CircadianRhythm.txt +0 -0
  87. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/Lorenz.txt +0 -0
  88. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/SanUmSrisuchinwong.txt +0 -0
  89. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/SprottMore.txt +0 -0
  90. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/Torus.txt +0 -0
  91. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/fixtures/YuWang2.txt +0 -0
  92. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/gen_fixtures.py +0 -0
  93. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/golden_fixtures.rs +0 -0
  94. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-ir/tests/roundtrip.rs +0 -0
  95. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/Cargo.toml +0 -0
  96. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/codegen.rs +0 -0
  97. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/error.rs +0 -0
  98. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/evaluator.rs +0 -0
  99. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/lib.rs +0 -0
  100. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/src/shims.rs +0 -0
  101. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/compile_latency.rs +0 -0
  102. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/golden_fixtures.rs +0 -0
  103. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-jit/tests/interpreter_equivalence.rs +0 -0
  104. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/Cargo.toml +0 -0
  105. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/caps.rs +0 -0
  106. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/adams.rs +0 -0
  107. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/bs3.rs +0 -0
  108. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/cashkarp.rs +0 -0
  109. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/control.rs +0 -0
  110. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/euler.rs +0 -0
  111. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/heun.rs +0 -0
  112. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/heun_euler.rs +0 -0
  113. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/midpoint.rs +0 -0
  114. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/mod.rs +0 -0
  115. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/ralston.rs +0 -0
  116. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk4.rs +0 -0
  117. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk45.rs +0 -0
  118. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rk4_38.rs +0 -0
  119. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/rkf45.rs +0 -0
  120. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/ssprk3.rs +0 -0
  121. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/testkit.rs +0 -0
  122. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/explicit/tsit5.rs +0 -0
  123. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/backward_euler.rs +0 -0
  124. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/bdf.rs +0 -0
  125. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/control.rs +0 -0
  126. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/implicit_midpoint.rs +0 -0
  127. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/linalg.rs +0 -0
  128. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/mod.rs +0 -0
  129. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/newton.rs +0 -0
  130. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/rosenbrock.rs +0 -0
  131. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/sdirk2.rs +0 -0
  132. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/trapezoid.rs +0 -0
  133. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/implicit/trbdf2.rs +0 -0
  134. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/lib.rs +0 -0
  135. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/euler_maruyama.rs +0 -0
  136. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/milstein.rs +0 -0
  137. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/mod.rs +0 -0
  138. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/src/sde/testkit.rs +0 -0
  139. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/explicit_accuracy.rs +0 -0
  140. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/implicit_stiff.rs +0 -0
  141. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-solvers/tests/registry.rs +0 -0
  142. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/Cargo.toml +0 -0
  143. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/src/interp.rs +0 -0
  144. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/src/lib.rs +0 -0
  145. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/tests/golden_fixtures.rs +0 -0
  146. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/crates/tsdyn-vm/tests/oracle_equivalence.rs +0 -0
  147. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/_result_common.py +0 -0
  148. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/__init__.py +0 -0
  149. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_plotbuilder.py +0 -0
  150. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result.py +0 -0
  151. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_array.py +0 -0
  152. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_collection.py +0 -0
  153. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_json.py +0 -0
  154. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_scalar.py +0 -0
  155. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_result_viz.py +0 -0
  156. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/_tangent.py +0 -0
  157. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/__init__.py +0 -0
  158. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/_common.py +0 -0
  159. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/attractors.py +0 -0
  160. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/basins.py +0 -0
  161. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/basins/continuation.py +0 -0
  162. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/__init__.py +0 -0
  163. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/_common.py +0 -0
  164. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/chaos/gali.py +0 -0
  165. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/__init__.py +0 -0
  166. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/_scaling.py +0 -0
  167. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/fixedmass.py +0 -0
  168. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/dimensions/generalized.py +0 -0
  169. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/__init__.py +0 -0
  170. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/_common.py +0 -0
  171. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/embedding/embed.py +0 -0
  172. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/__init__.py +0 -0
  173. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/core.py +0 -0
  174. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/dispersion.py +0 -0
  175. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/lz.py +0 -0
  176. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/multiscale.py +0 -0
  177. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/entropy/permutation.py +0 -0
  178. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/__init__.py +0 -0
  179. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/_common.py +0 -0
  180. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/fixedpoints/fixed.py +0 -0
  181. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/__init__.py +0 -0
  182. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/poincare.py +0 -0
  183. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/orbits/return_map.py +0 -0
  184. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/__init__.py +0 -0
  185. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/_common.py +0 -0
  186. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/matrix.py +0 -0
  187. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/rqa.py +0 -0
  188. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/recurrence/windowed.py +0 -0
  189. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/sampling/__init__.py +0 -0
  190. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/__init__.py +0 -0
  191. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/generators.py +0 -0
  192. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/hypothesis.py +0 -0
  193. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/analysis/surrogate/statistics.py +0 -0
  194. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/__init__.py +0 -0
  195. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/sampling.py +0 -0
  196. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/data/trajectory.py +0 -0
  197. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/__init__.py +0 -0
  198. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_base.py +0 -0
  199. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_crossings.py +0 -0
  200. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/_variational.py +0 -0
  201. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/ensemble.py +0 -0
  202. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/poincare.py +0 -0
  203. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/stroboscopic.py +0 -0
  204. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/derived/wrapped.py +0 -0
  205. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/__init__.py +0 -0
  206. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/_families.py +0 -0
  207. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/compile.py +0 -0
  208. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/problem.py +0 -0
  209. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/reference.py +0 -0
  210. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/run.py +0 -0
  211. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/run_methods.py +0 -0
  212. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/sde_run.py +0 -0
  213. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/stepper.py +0 -0
  214. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/engine/symbols.py +0 -0
  215. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/errors.py +0 -0
  216. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/__init__.py +0 -0
  217. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_accessors.py +0 -0
  218. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_dde_lyapunov.py +0 -0
  219. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/_plottable.py +0 -0
  220. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/base.py +0 -0
  221. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/delay.py +0 -0
  222. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/protocol.py +0 -0
  223. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/families/stochastic.py +0 -0
  224. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/py.typed +0 -0
  225. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/registry.py +0 -0
  226. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/__init__.py +0 -0
  227. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/explicit.py +0 -0
  228. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/implicit.py +0 -0
  229. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/select.py +0 -0
  230. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/solvers/stochastic.py +0 -0
  231. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/__init__.py +0 -0
  232. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/__init__.py +0 -0
  233. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/chem_bio_systems.py +0 -0
  234. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/coupled_systems.py +0 -0
  235. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/delayed_systems.py +0 -0
  236. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/physical_systems.py +0 -0
  237. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/population_dynamics.py +0 -0
  238. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/continuous/spatial_fields.py +0 -0
  239. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/__init__.py +0 -0
  240. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/polynomial_maps.py +0 -0
  241. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/systems/discrete/population_maps.py +0 -0
  242. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/__init__.py +0 -0
  243. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/_common.py +0 -0
  244. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/_result.py +0 -0
  245. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/features.py +0 -0
  246. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/transforms/preprocessing.py +0 -0
  247. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/utils/__init__.py +0 -0
  248. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/utils/grids.py +0 -0
  249. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/__init__.py +0 -0
  250. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/export.py +0 -0
  251. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/producers.py +0 -0
  252. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/__init__.py +0 -0
  253. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/caps.py +0 -0
  254. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/json.py +0 -0
  255. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/__init__.py +0 -0
  256. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_anim.py +0 -0
  257. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/mpl/_threed.py +0 -0
  258. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/__init__.py +0 -0
  259. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_composite.py +0 -0
  260. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_core.py +0 -0
  261. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_html.py +0 -0
  262. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/plotly/_threed.py +0 -0
  263. {tsdynamics-5.2.5 → tsdynamics-5.2.6}/src/tsdynamics/viz/render/threejs/__init__.py +0 -0
  264. {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
+ [![Python](https://img.shields.io/pypi/pyversions/tsdynamics)](https://pypi.org/project/tsdynamics/)
39
+ [![CI](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml/badge.svg)](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml)
40
+ [![Release](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml/badge.svg)](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml)
41
+ [![Docs](https://github.com/El3ssar/TSDynamics/actions/workflows/docs.yml/badge.svg)](https://el3ssar.github.io/TSDynamics/)
42
+ [![PyPI](https://img.shields.io/pypi/v/tsdynamics)](https://pypi.org/project/tsdynamics/)
43
+ [![codecov](https://codecov.io/gh/El3ssar/TSDynamics/graph/badge.svg)](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
+ ![Logistic bifurcation diagram](docs/assets/readme/logistic_bifurcation.png)
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
+ [![Python](https://img.shields.io/pypi/pyversions/tsdynamics)](https://pypi.org/project/tsdynamics/)
4
+ [![CI](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml/badge.svg)](https://github.com/El3ssar/TSDynamics/actions/workflows/ci.yml)
5
+ [![Release](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml/badge.svg)](https://github.com/El3ssar/TSDynamics/actions/workflows/release.yml)
6
+ [![Docs](https://github.com/El3ssar/TSDynamics/actions/workflows/docs.yml/badge.svg)](https://el3ssar.github.io/TSDynamics/)
7
+ [![PyPI](https://img.shields.io/pypi/v/tsdynamics)](https://pypi.org/project/tsdynamics/)
8
+ [![codecov](https://codecov.io/gh/El3ssar/TSDynamics/graph/badge.svg)](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
+ ![Logistic bifurcation diagram](docs/assets/readme/logistic_bifurcation.png)
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 implicit kernels (`rosenbrock`/`trbdf2`) freeze `∂f/∂u` each step and
325
- /// solve `(I − c·h·J)`. Driving them over a tape compiled *without* a Jacobian
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 is **bit-identical** to the Python mapper
9
- //! because it drives the *same* sequential algorithm over the *same* engine
10
- //! stepper: each cell-check advances a flow by one `dt` (an [`integrate_grid`] over
11
- //! the two-node grid `[t, t+dt]`, byte-for-byte the per-`dt` integration the Python
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` — no per-trajectory heap
127
- // `Vec` and no second copy. The partition is positional, so the output is
128
- // still in input order regardless of how rayon schedules the workers
129
- // (parallel == serial bit-for-bit, the determinism contract above).
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`, and the transcendental functions |
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. The 13th (FSAL) stage that
15
- //! SciPy keeps for dense output has zero weight in both error estimators, so only
16
- //! the 12 propagation stages are needed here.
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.5"
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.5"
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
@@ -311,6 +311,7 @@ class AnalysisResult:
311
311
  layers=[pb.markers(np.arange(vals.size, dtype=float), vals)],
312
312
  xlabel="field",
313
313
  xticks=list(range(len(names))),
314
+ xcategories=names,
314
315
  ylabel="value",
315
316
  title=type(self).__name__,
316
317
  meta=self.meta,