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.
Files changed (266) hide show
  1. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/PKG-INFO +1 -1
  2. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/pyproject.toml +1 -1
  3. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/__init__.py +7 -6
  4. tsdynamics-5.2.3/src/tsdynamics/analysis/_plotbuilder.py +434 -0
  5. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_array.py +32 -44
  6. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_base.py +19 -25
  7. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_collection.py +15 -21
  8. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_scalar.py +18 -21
  9. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_scaling.py +12 -35
  10. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/attractors.py +13 -11
  11. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/basins.py +31 -25
  12. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/continuation.py +19 -25
  13. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/metrics.py +10 -17
  14. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/gali.py +9 -13
  15. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/zero_one.py +8 -9
  16. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/_common.py +11 -24
  17. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/generalized.py +10 -12
  18. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/delay.py +9 -12
  19. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/dimension.py +16 -36
  20. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/embed.py +20 -31
  21. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/core.py +9 -9
  22. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/multiscale.py +8 -13
  23. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/_common.py +29 -4
  24. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/fixed.py +49 -63
  25. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/periodic.py +55 -58
  26. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/lyapunov/__init__.py +8 -11
  27. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/lyapunov/from_data.py +14 -19
  28. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/orbit_diagram.py +11 -12
  29. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/return_map.py +22 -35
  30. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/matrix.py +12 -14
  31. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/rqa.py +10 -8
  32. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/windowed.py +7 -8
  33. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/generators.py +11 -20
  34. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/hypothesis.py +12 -18
  35. tsdynamics-5.2.3/src/tsdynamics/engine/_families.py +414 -0
  36. tsdynamics-5.2.3/src/tsdynamics/engine/run.py +667 -0
  37. tsdynamics-5.2.3/src/tsdynamics/engine/run_methods.py +143 -0
  38. tsdynamics-5.2.3/src/tsdynamics/engine/sde_run.py +143 -0
  39. tsdynamics-5.2.3/src/tsdynamics/engine/stepper.py +162 -0
  40. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/errors.py +29 -0
  41. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/registry.py +58 -14
  42. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/chaotic_attractors.py +655 -1
  43. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/chem_bio_systems.py +837 -0
  44. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/climate_geophysics.py +428 -0
  45. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/coupled_systems.py +667 -0
  46. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/delayed_systems.py +172 -0
  47. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/exotic_systems.py +554 -0
  48. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/oscillatory_systems.py +354 -0
  49. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/physical_systems.py +137 -1
  50. tsdynamics-5.2.3/src/tsdynamics/systems/continuous/population_dynamics.py +161 -0
  51. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/spatial_fields.py +4 -0
  52. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/discrete/chaotic_maps.py +113 -0
  53. tsdynamics-5.2.3/src/tsdynamics/systems/discrete/exotic_maps.py +322 -0
  54. tsdynamics-5.2.3/src/tsdynamics/systems/discrete/geometric_maps.py +178 -0
  55. tsdynamics-5.2.3/src/tsdynamics/systems/discrete/polynomial_maps.py +138 -0
  56. tsdynamics-5.2.3/src/tsdynamics/systems/discrete/population_maps.py +117 -0
  57. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/utils/grids.py +17 -0
  58. tsdynamics-5.2.2/src/tsdynamics/engine/run.py +0 -1376
  59. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/chem_bio_systems.py +0 -403
  60. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/climate_geophysics.py +0 -217
  61. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/coupled_systems.py +0 -325
  62. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/delayed_systems.py +0 -83
  63. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/exotic_systems.py +0 -234
  64. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/oscillatory_systems.py +0 -184
  65. tsdynamics-5.2.2/src/tsdynamics/systems/continuous/population_dynamics.py +0 -81
  66. tsdynamics-5.2.2/src/tsdynamics/systems/discrete/exotic_maps.py +0 -180
  67. tsdynamics-5.2.2/src/tsdynamics/systems/discrete/geometric_maps.py +0 -102
  68. tsdynamics-5.2.2/src/tsdynamics/systems/discrete/polynomial_maps.py +0 -62
  69. tsdynamics-5.2.2/src/tsdynamics/systems/discrete/population_maps.py +0 -62
  70. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/LICENSE +0 -0
  71. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/README.md +0 -0
  72. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/Cargo.toml +0 -0
  73. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/Cargo.lock +0 -0
  74. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/Cargo.toml +0 -0
  75. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/dde.rs +0 -0
  76. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/events.rs +0 -0
  77. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/map.rs +0 -0
  78. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/marshal.rs +0 -0
  79. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/mod.rs +0 -0
  80. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/ode.rs +0 -0
  81. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/sde.rs +0 -0
  82. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/bridge/stepper.rs +0 -0
  83. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-core/src/lib.rs +0 -0
  84. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/Cargo.toml +0 -0
  85. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/dde.rs +0 -0
  86. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/ensemble.rs +0 -0
  87. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/event.rs +0 -0
  88. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/integrate.rs +0 -0
  89. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/lib.rs +0 -0
  90. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/map.rs +0 -0
  91. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/problem.rs +0 -0
  92. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/rng.rs +0 -0
  93. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/sde.rs +0 -0
  94. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-engine/src/testkit.rs +0 -0
  95. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/Cargo.toml +0 -0
  96. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/builder.rs +0 -0
  97. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/eval.rs +0 -0
  98. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/lib.rs +0 -0
  99. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/op.rs +0 -0
  100. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/reference.rs +0 -0
  101. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/src/tape.rs +0 -0
  102. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/coverage_tape.rs +0 -0
  103. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/CircadianRhythm.txt +0 -0
  104. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/Lorenz.txt +0 -0
  105. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/SanUmSrisuchinwong.txt +0 -0
  106. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/SprottMore.txt +0 -0
  107. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/Torus.txt +0 -0
  108. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/fixtures/YuWang2.txt +0 -0
  109. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/gen_fixtures.py +0 -0
  110. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/golden_fixtures.rs +0 -0
  111. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-ir/tests/roundtrip.rs +0 -0
  112. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/Cargo.toml +0 -0
  113. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/codegen.rs +0 -0
  114. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/error.rs +0 -0
  115. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/evaluator.rs +0 -0
  116. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/lib.rs +0 -0
  117. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/src/shims.rs +0 -0
  118. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/compile_latency.rs +0 -0
  119. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/golden_fixtures.rs +0 -0
  120. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-jit/tests/interpreter_equivalence.rs +0 -0
  121. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/Cargo.toml +0 -0
  122. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/caps.rs +0 -0
  123. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/adams.rs +0 -0
  124. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/bs3.rs +0 -0
  125. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/cashkarp.rs +0 -0
  126. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/control.rs +0 -0
  127. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/dop853.rs +0 -0
  128. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/euler.rs +0 -0
  129. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/heun.rs +0 -0
  130. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/heun_euler.rs +0 -0
  131. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/midpoint.rs +0 -0
  132. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/mod.rs +0 -0
  133. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/ralston.rs +0 -0
  134. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk4.rs +0 -0
  135. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk45.rs +0 -0
  136. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rk4_38.rs +0 -0
  137. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/rkf45.rs +0 -0
  138. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/ssprk3.rs +0 -0
  139. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/testkit.rs +0 -0
  140. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/explicit/tsit5.rs +0 -0
  141. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/backward_euler.rs +0 -0
  142. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/bdf.rs +0 -0
  143. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/control.rs +0 -0
  144. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/implicit_midpoint.rs +0 -0
  145. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/linalg.rs +0 -0
  146. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/mod.rs +0 -0
  147. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/newton.rs +0 -0
  148. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/rosenbrock.rs +0 -0
  149. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/sdirk2.rs +0 -0
  150. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/trapezoid.rs +0 -0
  151. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/implicit/trbdf2.rs +0 -0
  152. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/lib.rs +0 -0
  153. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/registry.rs +0 -0
  154. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/euler_maruyama.rs +0 -0
  155. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/milstein.rs +0 -0
  156. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/mod.rs +0 -0
  157. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/sde/testkit.rs +0 -0
  158. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/src/solver.rs +0 -0
  159. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/explicit_accuracy.rs +0 -0
  160. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/implicit_stiff.rs +0 -0
  161. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-solvers/tests/registry.rs +0 -0
  162. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/Cargo.toml +0 -0
  163. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/src/interp.rs +0 -0
  164. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/src/lib.rs +0 -0
  165. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/tests/golden_fixtures.rs +0 -0
  166. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/crates/tsdyn-vm/tests/oracle_equivalence.rs +0 -0
  167. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/_result_common.py +0 -0
  168. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/__init__.py +0 -0
  169. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result.py +0 -0
  170. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_json.py +0 -0
  171. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_result_viz.py +0 -0
  172. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/_tangent.py +0 -0
  173. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/__init__.py +0 -0
  174. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/basins/_common.py +0 -0
  175. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/__init__.py +0 -0
  176. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/_common.py +0 -0
  177. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/chaos/expansion.py +0 -0
  178. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/__init__.py +0 -0
  179. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/_scaling.py +0 -0
  180. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/correlation.py +0 -0
  181. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/dimensions/fixedmass.py +0 -0
  182. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/__init__.py +0 -0
  183. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/embedding/_common.py +0 -0
  184. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/__init__.py +0 -0
  185. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/dispersion.py +0 -0
  186. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/lz.py +0 -0
  187. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/permutation.py +0 -0
  188. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/entropy/sample.py +0 -0
  189. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/fixedpoints/__init__.py +0 -0
  190. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/__init__.py +0 -0
  191. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/orbits/poincare.py +0 -0
  192. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/__init__.py +0 -0
  193. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/recurrence/_common.py +0 -0
  194. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/sampling/__init__.py +0 -0
  195. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/sampling/sagitta.py +0 -0
  196. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/__init__.py +0 -0
  197. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/_common.py +0 -0
  198. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/analysis/surrogate/statistics.py +0 -0
  199. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/__init__.py +0 -0
  200. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/sampling.py +0 -0
  201. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/data/trajectory.py +0 -0
  202. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/__init__.py +0 -0
  203. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_base.py +0 -0
  204. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_crossings.py +0 -0
  205. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/_variational.py +0 -0
  206. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/ensemble.py +0 -0
  207. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/poincare.py +0 -0
  208. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/projected.py +0 -0
  209. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/stroboscopic.py +0 -0
  210. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/tangent.py +0 -0
  211. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/derived/wrapped.py +0 -0
  212. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/__init__.py +0 -0
  213. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/compile.py +0 -0
  214. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/events.py +0 -0
  215. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/problem.py +0 -0
  216. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/reference.py +0 -0
  217. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/engine/symbols.py +0 -0
  218. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/__init__.py +0 -0
  219. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_accessors.py +0 -0
  220. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_dde_lyapunov.py +0 -0
  221. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/_plottable.py +0 -0
  222. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/base.py +0 -0
  223. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/continuous.py +0 -0
  224. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/delay.py +0 -0
  225. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/discrete.py +0 -0
  226. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/protocol.py +0 -0
  227. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/stochastic.py +0 -0
  228. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/families/wrapped.py +0 -0
  229. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/plugins.py +0 -0
  230. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/py.typed +0 -0
  231. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/__init__.py +0 -0
  232. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/explicit.py +0 -0
  233. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/implicit.py +0 -0
  234. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/select.py +0 -0
  235. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/solvers/stochastic.py +0 -0
  236. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/__init__.py +0 -0
  237. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/continuous/__init__.py +0 -0
  238. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/systems/discrete/__init__.py +0 -0
  239. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/__init__.py +0 -0
  240. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/_common.py +0 -0
  241. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/_result.py +0 -0
  242. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/features.py +0 -0
  243. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/preprocessing.py +0 -0
  244. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/transforms/spectral.py +0 -0
  245. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/utils/__init__.py +0 -0
  246. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/__init__.py +0 -0
  247. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/compose.py +0 -0
  248. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/export.py +0 -0
  249. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/producers.py +0 -0
  250. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/__init__.py +0 -0
  251. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/caps.py +0 -0
  252. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/json.py +0 -0
  253. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/__init__.py +0 -0
  254. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_anim.py +0 -0
  255. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_core.py +0 -0
  256. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/mpl/_threed.py +0 -0
  257. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/__init__.py +0 -0
  258. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_anim.py +0 -0
  259. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_composite.py +0 -0
  260. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_core.py +0 -0
  261. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_html.py +0 -0
  262. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/plotly/_threed.py +0 -0
  263. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/threejs/__init__.py +0 -0
  264. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/render/threejs/_lower.py +0 -0
  265. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/spec.py +0 -0
  266. {tsdynamics-5.2.2 → tsdynamics-5.2.3}/src/tsdynamics/viz/style.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsdynamics
3
- Version: 5.2.2
3
+ Version: 5.2.3
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "tsdynamics"
7
- version = "5.2.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 (no renderer
50
- ships yet). Resolved **lazily** (via ``__getattr__``) so a plain
51
- ``import tsdynamics`` pulls in no plotting machinery.
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.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 ~149 model names in
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 ~149 built-in system classes,
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 tsdynamics.viz.spec import Axis, Layer, Legend, PlotKind, PlotSpec
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
- spec_kind = resolve_plot_kind(kind, PlotKind.DIAGNOSTIC_CURVE)
186
- layer = Layer(PlotKind.LINE, {"x": np.arange(arr.size, dtype=float), "y": arr})
187
- return PlotSpec(
188
- kind=spec_kind,
189
- ndim=2,
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
- x=Axis(label="index"),
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
- spec_kind = resolve_plot_kind(kind, PlotKind.PHASE_PORTRAIT_3D)
200
- layer = Layer(PlotKind.LINE3D, {"x": arr[:, 0], "y": arr[:, 1], "z": arr[:, 2]})
201
- return PlotSpec(
202
- kind=spec_kind,
203
- ndim=3,
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
- x=Axis(label="x1"),
206
- y=Axis(label="x2"),
207
- z=Axis(label="x3"),
208
- layers=[layer],
209
- meta=meta,
203
+ meta=self.meta,
210
204
  )
211
- spec_kind = resolve_plot_kind(kind, PlotKind.PHASE_PORTRAIT_2D)
212
- layer = Layer(PlotKind.SCATTER, {"x": arr[:, 0], "y": arr[:, 1]})
213
- return PlotSpec(
214
- kind=spec_kind,
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
- x=Axis(label="x1"),
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
- Layer(PlotKind.LINE, {"x": idx, "y": arr[:, j]}, label=f"[{j}]")
229
- for j in range(min(arr.shape[1], 12))
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
- legend=Legend(),
239
- meta=meta,
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 tsdynamics.viz.spec import Axis, Layer, PlotKind, PlotSpec
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: list[Layer] = []
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(Layer(PlotKind.LINE, {"x": x, "y": y}, label=yname))
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
- layers.append(
313
- Layer(PlotKind.MARKERS, {"x": np.arange(vals.size, dtype=float), "y": vals})
314
- )
315
- return PlotSpec(
316
- kind=spec_kind,
317
- ndim=2,
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
- x=Axis(label="field", ticks=list(range(len(names)))),
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 PlotSpec(
332
- kind=spec_kind,
333
- ndim=2,
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
- meta=dict(self.meta) if self.meta else {},
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: