tsdynamics 3.0.1__tar.gz → 3.0.2__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 (192) hide show
  1. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/PKG-INFO +1 -1
  2. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-core/src/bridge.rs +133 -3
  3. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-core/src/lib.rs +98 -0
  4. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/pyproject.toml +1 -1
  5. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/__init__.py +1 -1
  6. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/__init__.py +11 -0
  7. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/_result.py +5 -0
  8. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/__init__.py +5 -0
  9. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/_common.py +5 -0
  10. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/attractors.py +5 -0
  11. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/basins.py +5 -0
  12. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/continuation.py +5 -0
  13. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/basins/metrics.py +5 -0
  14. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/chaos/__init__.py +5 -0
  15. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/chaos/expansion.py +5 -0
  16. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/chaos/gali.py +5 -0
  17. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/chaos/zero_one.py +5 -0
  18. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/__init__.py +5 -0
  19. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/_common.py +5 -0
  20. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/_scaling.py +5 -0
  21. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/correlation.py +5 -0
  22. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/fixedmass.py +5 -0
  23. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/dimensions/generalized.py +5 -0
  24. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/embedding/__init__.py +5 -0
  25. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/embedding/_common.py +5 -0
  26. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/embedding/delay.py +5 -0
  27. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/embedding/dimension.py +5 -0
  28. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/embedding/embed.py +5 -0
  29. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/__init__.py +5 -0
  30. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/core.py +5 -0
  31. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/dispersion.py +5 -0
  32. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/lz.py +5 -0
  33. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/multiscale.py +5 -0
  34. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/permutation.py +5 -0
  35. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/entropy/sample.py +5 -0
  36. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/fixedpoints/__init__.py +5 -0
  37. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/fixedpoints/fixed.py +5 -0
  38. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/fixedpoints/periodic.py +5 -0
  39. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/lyapunov/__init__.py +5 -0
  40. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/lyapunov/from_data.py +5 -0
  41. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/orbits/__init__.py +5 -0
  42. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/orbits/orbit_diagram.py +5 -0
  43. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/orbits/poincare.py +5 -0
  44. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/orbits/return_map.py +5 -0
  45. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/recurrence/__init__.py +5 -0
  46. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/recurrence/_common.py +5 -0
  47. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/recurrence/matrix.py +5 -0
  48. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/recurrence/rqa.py +5 -0
  49. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/recurrence/windowed.py +5 -0
  50. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/surrogate/__init__.py +5 -0
  51. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/surrogate/_common.py +5 -0
  52. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/surrogate/generators.py +5 -0
  53. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/surrogate/hypothesis.py +5 -0
  54. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/surrogate/statistics.py +5 -0
  55. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/data/__init__.py +5 -0
  56. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/data/sampling.py +5 -0
  57. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/__init__.py +5 -0
  58. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/_base.py +5 -0
  59. tsdynamics-3.0.2/src/tsdynamics/derived/_crossings.py +194 -0
  60. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/_variational.py +5 -0
  61. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/ensemble.py +5 -0
  62. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/poincare.py +85 -9
  63. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/projected.py +5 -0
  64. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/stroboscopic.py +5 -0
  65. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/tangent.py +5 -0
  66. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/derived/wrapped.py +5 -0
  67. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/engine/__init__.py +10 -0
  68. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/engine/compile.py +5 -0
  69. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/engine/problem.py +5 -0
  70. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/engine/run.py +121 -0
  71. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/errors.py +5 -0
  72. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/__init__.py +5 -0
  73. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/_accessors.py +5 -0
  74. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/_dde_lyapunov.py +5 -0
  75. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/base.py +5 -0
  76. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/delay.py +5 -0
  77. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/protocol.py +5 -0
  78. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/stochastic.py +5 -0
  79. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/plugins.py +24 -0
  80. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/registry.py +9 -0
  81. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/solvers/__init__.py +5 -0
  82. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/solvers/select.py +5 -0
  83. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/__init__.py +9 -0
  84. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/__init__.py +5 -0
  85. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/__init__.py +5 -0
  86. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/transforms/__init__.py +5 -0
  87. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/transforms/features.py +5 -0
  88. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/transforms/preprocessing.py +5 -0
  89. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/transforms/spectral.py +5 -0
  90. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/utils/__init__.py +5 -0
  91. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/utils/grids.py +5 -0
  92. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/utils/sagitta_dt.py +5 -0
  93. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/viz/__init__.py +5 -0
  94. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/viz/spec.py +5 -0
  95. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/LICENSE +0 -0
  96. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/README.md +0 -0
  97. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/Cargo.toml +0 -0
  98. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-core/Cargo.lock +0 -0
  99. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-core/Cargo.toml +0 -0
  100. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/Cargo.toml +0 -0
  101. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/dde.rs +0 -0
  102. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/ensemble.rs +0 -0
  103. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/event.rs +0 -0
  104. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/integrate.rs +0 -0
  105. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/lib.rs +0 -0
  106. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/map.rs +0 -0
  107. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/problem.rs +0 -0
  108. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/rng.rs +0 -0
  109. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/sde.rs +0 -0
  110. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-engine/src/testkit.rs +0 -0
  111. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/Cargo.toml +0 -0
  112. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/builder.rs +0 -0
  113. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/eval.rs +0 -0
  114. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/lib.rs +0 -0
  115. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/op.rs +0 -0
  116. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/reference.rs +0 -0
  117. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/src/tape.rs +0 -0
  118. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/coverage_tape.rs +0 -0
  119. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/CircadianRhythm.txt +0 -0
  120. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/Lorenz.txt +0 -0
  121. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/SanUmSrisuchinwong.txt +0 -0
  122. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/SprottMore.txt +0 -0
  123. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/Torus.txt +0 -0
  124. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/fixtures/YuWang2.txt +0 -0
  125. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/gen_fixtures.py +0 -0
  126. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/golden_fixtures.rs +0 -0
  127. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-ir/tests/roundtrip.rs +0 -0
  128. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/Cargo.toml +0 -0
  129. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/src/codegen.rs +0 -0
  130. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/src/error.rs +0 -0
  131. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/src/evaluator.rs +0 -0
  132. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/src/lib.rs +0 -0
  133. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/src/shims.rs +0 -0
  134. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/tests/compile_latency.rs +0 -0
  135. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/tests/golden_fixtures.rs +0 -0
  136. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-jit/tests/interpreter_equivalence.rs +0 -0
  137. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/Cargo.toml +0 -0
  138. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/caps.rs +0 -0
  139. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/control.rs +0 -0
  140. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/dop853.rs +0 -0
  141. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/mod.rs +0 -0
  142. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/rk4.rs +0 -0
  143. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/rk45.rs +0 -0
  144. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/testkit.rs +0 -0
  145. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/explicit/tsit5.rs +0 -0
  146. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/bdf.rs +0 -0
  147. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/control.rs +0 -0
  148. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/linalg.rs +0 -0
  149. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/mod.rs +0 -0
  150. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/rosenbrock.rs +0 -0
  151. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/implicit/trbdf2.rs +0 -0
  152. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/lib.rs +0 -0
  153. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/registry.rs +0 -0
  154. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/sde/euler_maruyama.rs +0 -0
  155. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/sde/milstein.rs +0 -0
  156. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/sde/mod.rs +0 -0
  157. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/sde/testkit.rs +0 -0
  158. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/src/solver.rs +0 -0
  159. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/tests/explicit_accuracy.rs +0 -0
  160. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/tests/implicit_stiff.rs +0 -0
  161. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-solvers/tests/registry.rs +0 -0
  162. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-vm/Cargo.toml +0 -0
  163. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-vm/src/interp.rs +0 -0
  164. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-vm/src/lib.rs +0 -0
  165. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-vm/tests/golden_fixtures.rs +0 -0
  166. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/crates/tsdyn-vm/tests/oracle_equivalence.rs +0 -0
  167. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/chaos/_common.py +0 -0
  168. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/analysis/fixedpoints/_common.py +0 -0
  169. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/data/trajectory.py +0 -0
  170. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/engine/symbols.py +0 -0
  171. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/continuous.py +0 -0
  172. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/families/discrete.py +0 -0
  173. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/py.typed +0 -0
  174. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/solvers/explicit.py +0 -0
  175. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/solvers/implicit.py +0 -0
  176. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/solvers/stochastic.py +0 -0
  177. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/chaotic_attractors.py +0 -0
  178. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/chem_bio_systems.py +0 -0
  179. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/climate_geophysics.py +0 -0
  180. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/coupled_systems.py +0 -0
  181. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/delayed_systems.py +0 -0
  182. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/exotic_systems.py +0 -0
  183. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/oscillatory_systems.py +0 -0
  184. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/physical_systems.py +0 -0
  185. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/continuous/population_dynamics.py +0 -0
  186. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/chaotic_maps.py +0 -0
  187. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/exotic_maps.py +0 -0
  188. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/geometric_maps.py +0 -0
  189. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/polynomial_maps.py +0 -0
  190. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/systems/discrete/population_maps.py +0 -0
  191. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/transforms/_common.py +0 -0
  192. {tsdynamics-3.0.1 → tsdynamics-3.0.2}/src/tsdynamics/utils/general.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tsdynamics
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Science/Research
6
6
  Classifier: Operating System :: OS Independent
@@ -28,9 +28,10 @@
28
28
 
29
29
  use tsdyn_engine::rng::SplitMix64;
30
30
  use tsdyn_engine::{
31
- ensemble_final as engine_ensemble, integrate_dde_grid, integrate_grid, iterate_dense,
32
- iterate_ensemble_final as engine_iterate_ensemble, sde_ensemble_final as engine_sde_ensemble,
33
- sde_integrate_grid, DelaySlot, IntegrateConfig, IntegrateError, MapError, SdeConfig, SdeError,
31
+ ensemble_final as engine_ensemble, integrate_dde_grid, integrate_events, integrate_grid,
32
+ iterate_dense, iterate_ensemble_final as engine_iterate_ensemble,
33
+ sde_ensemble_final as engine_sde_ensemble, sde_integrate_grid, DelaySlot, EventDirection,
34
+ EventSpec, IntegrateConfig, IntegrateError, MapError, SdeConfig, SdeError,
34
35
  };
35
36
  use tsdyn_ir::{Evaluator, Tape};
36
37
  use tsdyn_jit::JitEvaluator;
@@ -950,6 +951,135 @@ pub fn sde_ensemble_final(
950
951
  Ok(result.states)
951
952
  }
952
953
 
954
+ /// Map a Python-side direction code (`+1` / `-1` / `0`) to an [`EventDirection`].
955
+ fn event_direction(direction: i32) -> Result<EventDirection, EngineError> {
956
+ match direction {
957
+ 1 => Ok(EventDirection::Rising),
958
+ -1 => Ok(EventDirection::Falling),
959
+ 0 => Ok(EventDirection::Either),
960
+ other => Err(EngineError::BadShape(format!(
961
+ "event direction must be +1 (rising), -1 (falling) or 0 (either), got {other}"
962
+ ))),
963
+ }
964
+ }
965
+
966
+ /// Integrate `[t0, t1]` and return every crossing of the event function
967
+ /// `g(u, t) = 0` in `direction`, refined with the engine's O(h⁴) cubic-Hermite
968
+ /// dense output (stream WS-CROSSKERNEL — wiring the previously dead
969
+ /// [`tsdyn_engine::integrate_events`]).
970
+ ///
971
+ /// `rhs` is the system right-hand side; `g` a single-output tape evaluated over
972
+ /// the full state (`g.dim() == 1`, `g.n_param() <= rhs.n_param()`). `first_step`
973
+ /// seeds the solver; with the fixed-step `rk4` it *is* the march step, so a caller
974
+ /// that wants the Python `PoincareMap`'s dt-grid detection (no skipped crossings,
975
+ /// answer-identical refinement) passes `method = "rk4"`, `first_step = dt`.
976
+ ///
977
+ /// Returns `(times, states_flat, t_final, u_final, terminated)` — the `K` crossing
978
+ /// times, the row-major `(K, dim)` crossing states, the time/state the run stopped
979
+ /// at (so a caller can resume the next span exactly), and whether a terminal event
980
+ /// stopped it. Divergence raises ([`EngineError::Diverged`]), preserving the
981
+ /// "diverge loudly" contract of the per-`dt` Python path.
982
+ #[allow(clippy::too_many_arguments, clippy::type_complexity)]
983
+ pub fn integrate_events_dense(
984
+ rhs: Tape,
985
+ g: Tape,
986
+ ic: &[f64],
987
+ p: &[f64],
988
+ t0: f64,
989
+ t1: f64,
990
+ first_step: f64,
991
+ direction: i32,
992
+ terminal: bool,
993
+ method: &str,
994
+ rtol: f64,
995
+ atol: f64,
996
+ jit: bool,
997
+ ) -> Result<(Vec<f64>, Vec<f64>, f64, Vec<f64>, bool), EngineError> {
998
+ guard_continuous(&rhs)?;
999
+ check_inputs(&rhs, ic, p)?;
1000
+ if let Some(&bad) = ic.iter().find(|x| !x.is_finite()) {
1001
+ return Err(EngineError::BadShape(format!(
1002
+ "initial state must be finite, found {bad}"
1003
+ )));
1004
+ }
1005
+ let dim = rhs.dim();
1006
+ if dim == 0 {
1007
+ return Err(EngineError::BadShape(
1008
+ "system dimension is zero (the tape has no outputs)".to_string(),
1009
+ ));
1010
+ }
1011
+ // The event function is a single-output tape over the full state; it shares
1012
+ // the system parameter vector (it may declare fewer and read the leading
1013
+ // slice). `eval_event` in the engine asserts both, so reject violations here
1014
+ // as a clean `ValueError` rather than a release-build panic.
1015
+ if g.dim() != 1 {
1016
+ return Err(EngineError::BadShape(format!(
1017
+ "event function must have a single output, got {}",
1018
+ g.dim()
1019
+ )));
1020
+ }
1021
+ if g.n_state() > dim {
1022
+ return Err(EngineError::BadShape(format!(
1023
+ "event function reads {} state inputs but the system has dim = {dim}",
1024
+ g.n_state()
1025
+ )));
1026
+ }
1027
+ if g.n_param() > p.len() {
1028
+ return Err(EngineError::BadShape(format!(
1029
+ "event function declares {} parameters but the system has {}",
1030
+ g.n_param(),
1031
+ p.len()
1032
+ )));
1033
+ }
1034
+ if !(t0.is_finite() && t1.is_finite()) {
1035
+ return Err(EngineError::BadShape(format!(
1036
+ "event integration span must be finite, got t0 = {t0}, t1 = {t1}"
1037
+ )));
1038
+ }
1039
+ if t1 < t0 {
1040
+ return Err(EngineError::BadShape(format!(
1041
+ "integrate_events is forward only: need t1 >= t0, got t0 = {t0}, t1 = {t1}"
1042
+ )));
1043
+ }
1044
+ // The engine asserts a finite, positive first step (a hard `assert!`, not a
1045
+ // debug one) — turn a bad value into a clean error at the boundary.
1046
+ if !(first_step.is_finite() && first_step > 0.0) {
1047
+ return Err(EngineError::BadShape(format!(
1048
+ "first step (the detection dt) must be finite and positive, got {first_step}"
1049
+ )));
1050
+ }
1051
+ let dir = event_direction(direction)?;
1052
+ let name = resolve_solver(method)?;
1053
+ require_jacobian_if_needed(&rhs, name)?;
1054
+
1055
+ let rhs_ev = build_evaluator(rhs, jit)?;
1056
+ let g_ev = build_evaluator(g, jit)?;
1057
+ let mut solver = build_solver(name, rtol, atol);
1058
+ let cfg = IntegrateConfig::new(first_step);
1059
+
1060
+ let spec = if terminal {
1061
+ EventSpec::terminal(&*g_ev, dir)
1062
+ } else {
1063
+ EventSpec::new(&*g_ev, dir)
1064
+ };
1065
+ let outcome = integrate_events(&*rhs_ev, &mut *solver, &ic[..dim], p, t0, t1, &[spec], &cfg)
1066
+ .map_err(|e| EngineError::Diverged(diverge_msg(&e)))?;
1067
+
1068
+ let mut times = Vec::with_capacity(outcome.hits.len());
1069
+ let mut states = Vec::with_capacity(outcome.hits.len() * dim);
1070
+ for hit in &outcome.hits {
1071
+ times.push(hit.t);
1072
+ states.extend_from_slice(&hit.u);
1073
+ }
1074
+ Ok((
1075
+ times,
1076
+ states,
1077
+ outcome.t_final,
1078
+ outcome.u_final,
1079
+ outcome.terminated,
1080
+ ))
1081
+ }
1082
+
953
1083
  /// Prefix the engine's diverge message so the Python `RuntimeError` reads clearly.
954
1084
  fn diverge_msg(e: &IntegrateError) -> String {
955
1085
  format!("integration diverged before reaching the final time: {e}")
@@ -591,6 +591,103 @@ fn integrate_sde_ensemble_final<'py>(
591
591
  PyArray1::from_vec(py, flat).reshape([n_ic, dim])
592
592
  }
593
593
 
594
+ // ---------------------------------------------------------------------------
595
+ // Event detection (two tapes: right-hand side + event function g)
596
+ // ---------------------------------------------------------------------------
597
+
598
+ /// Integrate `[t0, t1]` and return the crossings of the event function
599
+ /// `g(u, t) = 0` in `direction` (`+1` rising / `-1` falling / `0` either),
600
+ /// refined with the engine's O(h⁴) cubic-Hermite dense output.
601
+ ///
602
+ /// The two tapes are passed back to back — first the right-hand side `f`, then the
603
+ /// single-output event function `g` (`g`'s `outputs.len() == 1`; it reads the full
604
+ /// state and may declare fewer parameters than the system, reading the leading
605
+ /// slice). `first_step` seeds the solver; with the fixed-step `rk4` it *is* the
606
+ /// detection step, so `method="rk4"`, `first_step=dt` reproduces the Python
607
+ /// `PoincareMap` dt-grid march and its refinement (answer-identical). `terminal`
608
+ /// stops the run at the first crossing. Divergence raises `RuntimeError`.
609
+ ///
610
+ /// Returns `(times, states, t_final, u_final, terminated)`: the `(K,)` crossing
611
+ /// times, the `(K, dim)` crossing states, the time and state the run stopped at
612
+ /// (to resume the next span exactly), and whether a terminal event stopped it.
613
+ #[pyfunction]
614
+ #[allow(clippy::too_many_arguments, clippy::type_complexity)]
615
+ fn integrate_events_dense<'py>(
616
+ py: Python<'py>,
617
+ ops: PyReadonlyArray1<i32>,
618
+ a: PyReadonlyArray1<i32>,
619
+ b: PyReadonlyArray1<i32>,
620
+ imm: PyReadonlyArray1<f64>,
621
+ outputs: PyReadonlyArray1<i32>,
622
+ jac_outputs: PyReadonlyArray1<i32>,
623
+ n_state: usize,
624
+ n_param: usize,
625
+ ops_g: PyReadonlyArray1<i32>,
626
+ a_g: PyReadonlyArray1<i32>,
627
+ b_g: PyReadonlyArray1<i32>,
628
+ imm_g: PyReadonlyArray1<f64>,
629
+ outputs_g: PyReadonlyArray1<i32>,
630
+ jac_outputs_g: PyReadonlyArray1<i32>,
631
+ n_state_g: usize,
632
+ n_param_g: usize,
633
+ ic: PyReadonlyArray1<f64>,
634
+ p: PyReadonlyArray1<f64>,
635
+ t0: f64,
636
+ t1: f64,
637
+ first_step: f64,
638
+ direction: i32,
639
+ terminal: bool,
640
+ method: String,
641
+ rtol: f64,
642
+ atol: f64,
643
+ jit: bool,
644
+ ) -> PyResult<(
645
+ Bound<'py, PyArray1<f64>>,
646
+ Bound<'py, PyArray2<f64>>,
647
+ f64,
648
+ Bound<'py, PyArray1<f64>>,
649
+ bool,
650
+ )> {
651
+ let rhs = OwnedTape::copy_in(&ops, &a, &b, &imm, &outputs, &jac_outputs, n_state, n_param)?;
652
+ let g = OwnedTape::copy_in(
653
+ &ops_g,
654
+ &a_g,
655
+ &b_g,
656
+ &imm_g,
657
+ &outputs_g,
658
+ &jac_outputs_g,
659
+ n_state_g,
660
+ n_param_g,
661
+ )?;
662
+ let dim = rhs.dim();
663
+ let ic = vec_f64("ic", &ic)?;
664
+ let p = vec_f64("p", &p)?;
665
+ let (times, states, t_final, u_final, terminated) = py
666
+ .detach(|| {
667
+ bridge::integrate_events_dense(
668
+ rhs.build()?,
669
+ g.build()?,
670
+ &ic,
671
+ &p,
672
+ t0,
673
+ t1,
674
+ first_step,
675
+ direction,
676
+ terminal,
677
+ &method,
678
+ rtol,
679
+ atol,
680
+ jit,
681
+ )
682
+ })
683
+ .map_err(to_py_err)?;
684
+ let n_hits = times.len();
685
+ let times = PyArray1::from_vec(py, times);
686
+ let states = PyArray1::from_vec(py, states).reshape([n_hits, dim])?;
687
+ let u_final = PyArray1::from_vec(py, u_final);
688
+ Ok((times, states, t_final, u_final, terminated))
689
+ }
690
+
594
691
  // ---------------------------------------------------------------------------
595
692
  // Introspection
596
693
  // ---------------------------------------------------------------------------
@@ -644,6 +741,7 @@ fn _rust(m: &Bound<'_, PyModule>) -> PyResult<()> {
644
741
  m.add_function(wrap_pyfunction!(integrate_ensemble_final, m)?)?;
645
742
  m.add_function(wrap_pyfunction!(integrate_sde_dense, m)?)?;
646
743
  m.add_function(wrap_pyfunction!(integrate_sde_ensemble_final, m)?)?;
744
+ m.add_function(wrap_pyfunction!(integrate_events_dense, m)?)?;
647
745
  m.add_function(wrap_pyfunction!(solvers, m)?)?;
648
746
  m.add_function(wrap_pyfunction!(_version, m)?)?;
649
747
  Ok(())
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "tsdynamics"
7
- version = "3.0.1"
7
+ version = "3.0.2"
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"
@@ -124,7 +124,7 @@ from .families import (
124
124
  )
125
125
 
126
126
  # Single source of truth for the package version; rewritten by python-semantic-release.
127
- __version__ = "3.0.1"
127
+ __version__ = "3.0.2"
128
128
 
129
129
  # Built-in system classes are NOT bound into this namespace — that would bury the
130
130
  # submodules (``analysis``, ``data``, ``systems``, …) under ~149 model names in
@@ -177,6 +177,7 @@ __all__ = [
177
177
  "correlation_dimension",
178
178
  "correlation_sum",
179
179
  "dimension_spectrum",
180
+ "discover_plugins",
180
181
  "dispersion_entropy",
181
182
  "embed",
182
183
  "embedding_dimension",
@@ -250,3 +251,13 @@ def discover_plugins(*, strict: bool = False) -> list[str]:
250
251
  # analyses register themselves from their own subpackages (the analysis streams);
251
252
  # plugin failures are isolated inside `register_entry_points`.
252
253
  discover_plugins()
254
+
255
+
256
+ def __dir__() -> list[str]:
257
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete.
258
+
259
+ The capability subpackages (``basins``, ``chaos``, …) and plugin-wiring
260
+ helpers are re-exported flat or used internally; they stay importable but are
261
+ kept off the tab-completion surface so only the public quantifiers show.
262
+ """
263
+ return sorted(__all__)
@@ -685,3 +685,8 @@ class ScalingResult(AnalysisResult):
685
685
  layers=layers,
686
686
  meta=dict(self.meta) if self.meta else {},
687
687
  )
688
+
689
+
690
+ def __dir__() -> list[str]:
691
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
692
+ return sorted(__all__)
@@ -77,3 +77,8 @@ for _name, _fn, _needs in (
77
77
  ):
78
78
  _registry.analyses.register(_name, _fn, needs=_needs, family="basins")
79
79
  del _name, _fn, _needs
80
+
81
+
82
+ def __dir__() -> list[str]:
83
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
84
+ return sorted(__all__)
@@ -173,3 +173,8 @@ def _apply_merge(labels: np.ndarray, merge: dict[int, int]) -> np.ndarray:
173
173
  if old != new:
174
174
  out[labels == old] = new
175
175
  return out
176
+
177
+
178
+ def __dir__() -> list[str]:
179
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
180
+ return sorted(__all__)
@@ -463,3 +463,8 @@ def resolve_merge_tol(cellgrid: _CellGrid, merge_tol: float | None) -> float:
463
463
  def _looks_unsupported(system: Any) -> bool:
464
464
  """Return True for delay / stochastic systems (no finite-dimensional state)."""
465
465
  return hasattr(system, "_drift") or hasattr(system, "history") or hasattr(system, "_delays")
466
+
467
+
468
+ def __dir__() -> list[str]:
469
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
470
+ return sorted(__all__)
@@ -353,3 +353,8 @@ def basin_fractions(
353
353
  fractions = {k: c / n for k, c in merged_counts.items()}
354
354
  attractors = mapper.attractor_set(diverged=diverged, seeds=n, merge=merge)
355
355
  return BasinFractions(fractions=fractions, diverged=diverged / n, n=n, attractors=attractors)
356
+
357
+
358
+ def __dir__() -> list[str]:
359
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
360
+ return sorted(__all__)
@@ -270,3 +270,8 @@ def tipping_points(result: ContinuationResult, *, threshold: float = 0.0) -> lis
270
270
  )
271
271
  events.sort(key=lambda e: (e["value"], e["attractor"]))
272
272
  return events
273
+
274
+
275
+ def __dir__() -> list[str]:
276
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
277
+ return sorted(__all__)
@@ -471,3 +471,8 @@ def resilience(result: BasinsResult, attractor_id: int) -> float:
471
471
  idx = np.rint((center - grid.lo) / spacing).astype(int)
472
472
  idx = np.clip(idx, 0, np.asarray(grid.shape) - 1)
473
473
  return float(edt[tuple(idx)])
474
+
475
+
476
+ def __dir__() -> list[str]:
477
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
478
+ return sorted(__all__)
@@ -50,3 +50,8 @@ for _name, _fn, _meta in (
50
50
  ):
51
51
  _registry.analyses.register(_name, _fn, **_meta)
52
52
  del _name, _fn, _meta
53
+
54
+
55
+ def __dir__() -> list[str]:
56
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
57
+ return sorted(__all__)
@@ -283,3 +283,8 @@ def _expansion_flow(
283
283
  times[s] = s * dt
284
284
  ln_e[s] = np.log(e) if e > 0.0 else -np.inf
285
285
  return times, ln_e, int(np.count_nonzero(alive))
286
+
287
+
288
+ def __dir__() -> list[str]:
289
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
290
+ return sorted(__all__)
@@ -350,3 +350,8 @@ def _gali_flow(
350
350
  values[i] = _c.gali_volume(w)
351
351
  times[i] = t
352
352
  return times, values
353
+
354
+
355
+ def __dir__() -> list[str]:
356
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
357
+ return sorted(__all__)
@@ -120,3 +120,8 @@ def zero_one_test(
120
120
 
121
121
  k = float(np.median(k_c))
122
122
  return (k, k_c) if return_distribution else k
123
+
124
+
125
+ def __dir__() -> list[str]:
126
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
127
+ return sorted(__all__)
@@ -81,3 +81,8 @@ for _name, _fn, _meta in (
81
81
  ):
82
82
  _registry.analyses.register(_name, _fn, **_meta)
83
83
  del _name, _fn, _meta
84
+
85
+
86
+ def __dir__() -> list[str]:
87
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
88
+ return sorted(__all__)
@@ -300,3 +300,8 @@ class DimensionResult:
300
300
  f"dimension={self.dimension:.4g} ± {self.stderr:.2g}, "
301
301
  f"n_fit={self.fit_slice[1] - self.fit_slice[0] + 1})"
302
302
  )
303
+
304
+
305
+ def __dir__() -> list[str]:
306
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
307
+ return sorted(__all__)
@@ -182,3 +182,8 @@ def local_slopes(x: np.ndarray, y: np.ndarray) -> np.ndarray:
182
182
  if x.size < 2:
183
183
  return np.full(x.shape, np.nan)
184
184
  return np.gradient(y, x)
185
+
186
+
187
+ def __dir__() -> list[str]:
188
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
189
+ return sorted(__all__)
@@ -198,3 +198,8 @@ def correlation_dimension(
198
198
  intercept=fit.intercept,
199
199
  q=2.0,
200
200
  )
201
+
202
+
203
+ def __dir__() -> list[str]:
204
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
205
+ return sorted(__all__)
@@ -162,3 +162,8 @@ def fixed_mass_dimension(
162
162
  intercept=fit.intercept,
163
163
  q=None,
164
164
  )
165
+
166
+
167
+ def __dir__() -> list[str]:
168
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
169
+ return sorted(__all__)
@@ -243,3 +243,8 @@ def dimension_spectrum(
243
243
  q=q,
244
244
  )
245
245
  return out
246
+
247
+
248
+ def __dir__() -> list[str]:
249
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
250
+ return sorted(__all__)
@@ -59,3 +59,8 @@ for _name, _fn in (
59
59
  ):
60
60
  _registry.analyses.register(_name, _fn, needs="series", family="embedding")
61
61
  del _name, _fn
62
+
63
+
64
+ def __dir__() -> list[str]:
65
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
66
+ return sorted(__all__)
@@ -131,3 +131,8 @@ def _as_channels(x: Any) -> np.ndarray:
131
131
  if not np.all(np.isfinite(arr)):
132
132
  raise ValueError("channel data contains non-finite values (nan/inf).")
133
133
  return arr
134
+
135
+
136
+ def __dir__() -> list[str]:
137
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
138
+ return sorted(__all__)
@@ -227,3 +227,8 @@ def optimal_delay(
227
227
  return max(int(acf.size) - 1, 1) # never crosses → longest lag available
228
228
 
229
229
  raise ValueError(f"unknown method {method!r}; use 'mi', 'acf', or 'acf_zero'.")
230
+
231
+
232
+ def __dir__() -> list[str]:
233
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
234
+ return sorted(__all__)
@@ -336,3 +336,8 @@ def embedding_dimension(
336
336
  data, delay=delay, max_dim=max_dim, component=component, **kwargs
337
337
  )
338
338
  raise ValueError(f"unknown method {method!r}; use 'cao' or 'fnn'.")
339
+
340
+
341
+ def __dir__() -> list[str]:
342
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
343
+ return sorted(__all__)
@@ -161,3 +161,8 @@ def _embed_single(series: np.ndarray, m: int, tau: int) -> np.ndarray:
161
161
  start = j * tau
162
162
  out[:, j] = series[start : start + rows]
163
163
  return out
164
+
165
+
166
+ def __dir__() -> list[str]:
167
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
168
+ return sorted(__all__)
@@ -91,3 +91,8 @@ for _name, _fn in {
91
91
  "lz76_entropy": lz76_entropy,
92
92
  }.items():
93
93
  _registry.analyses.register(_name, _fn, needs="series")
94
+
95
+
96
+ def __dir__() -> list[str]:
97
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
98
+ return sorted(__all__)
@@ -552,3 +552,8 @@ def entropy(
552
552
  hmax = measure.maximum(outcomes.cardinality)
553
553
  return h / hmax if hmax > 0 else 0.0
554
554
  return h
555
+
556
+
557
+ def __dir__() -> list[str]:
558
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
559
+ return sorted(__all__)
@@ -71,3 +71,8 @@ def dispersion_entropy(
71
71
  normalize=normalize,
72
72
  component=component,
73
73
  )
74
+
75
+
76
+ def __dir__() -> list[str]:
77
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
78
+ return sorted(__all__)
@@ -297,3 +297,8 @@ def _lz76_via_lzcomplexity(codes: np.ndarray, k: int) -> int:
297
297
  # list[int] → concatenated-decimals gotcha) and pass the resulting string.
298
298
  text = "".join(chr(0x30 + int(code)) for code in codes)
299
299
  return int(lz.lz76Factorization(text))
300
+
301
+
302
+ def __dir__() -> list[str]:
303
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
304
+ return sorted(__all__)
@@ -122,3 +122,8 @@ def multiscale_entropy(
122
122
  for k, s in enumerate(scale_list):
123
123
  out[k] = entropy_fn(coarse_grain(series, s), **call_kwargs)
124
124
  return out
125
+
126
+
127
+ def __dir__() -> list[str]:
128
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
129
+ return sorted(__all__)
@@ -121,3 +121,8 @@ def weighted_permutation_entropy(
121
121
  hmax = Shannon(base).maximum(space.cardinality)
122
122
  return h / hmax if hmax > 0 else 0.0
123
123
  return h
124
+
125
+
126
+ def __dir__() -> list[str]:
127
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
128
+ return sorted(__all__)
@@ -166,3 +166,8 @@ def approximate_entropy(
166
166
  return log_sum / n_templates
167
167
 
168
168
  return float(phi(m) - phi(m + 1))
169
+
170
+
171
+ def __dir__() -> list[str]:
172
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
173
+ return sorted(__all__)
@@ -53,3 +53,8 @@ for _name, _fn, _meta in (
53
53
  ):
54
54
  _registry.analyses.register(_name, _fn, **_meta)
55
55
  del _name, _fn, _meta
56
+
57
+
58
+ def __dir__() -> list[str]:
59
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
60
+ return sorted(__all__)
@@ -242,3 +242,8 @@ def _stabilising_matrices(method: str, dim: int, max_c: int | None) -> list[np.n
242
242
  stacklevel=3,
243
243
  )
244
244
  return mats
245
+
246
+
247
+ def __dir__() -> list[str]:
248
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
249
+ return sorted(__all__)
@@ -600,3 +600,8 @@ def _fft_period_lag(y: np.ndarray) -> float:
600
600
  if k == 0:
601
601
  raise ValueError("estimate_period: no dominant frequency found.")
602
602
  return float(n / k)
603
+
604
+
605
+ def __dir__() -> list[str]:
606
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
607
+ return sorted(__all__)
@@ -187,3 +187,8 @@ for _name, _fn, _meta in (
187
187
  ):
188
188
  _registry.analyses.register(_name, _fn, **_meta)
189
189
  del _name, _fn, _meta
190
+
191
+
192
+ def __dir__() -> list[str]:
193
+ """Expose only the curated public API (``__all__``) to ``dir()`` / autocomplete."""
194
+ return sorted(__all__)