physicskit 0.1.0__py3-none-any.whl

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 (484) hide show
  1. physicskit/__init__.py +51 -0
  2. physicskit/astro/__init__.py +66 -0
  3. physicskit/astro/cosmic_web.py +395 -0
  4. physicskit/astro/galactic_dynamics.py +137 -0
  5. physicskit/astro/nbody.py +208 -0
  6. physicskit/astro/orbital_mechanics.py +220 -0
  7. physicskit/astro/stellar_dynamo.py +628 -0
  8. physicskit/astro/stellar_structure.py +198 -0
  9. physicskit/astro/tests/__init__.py +0 -0
  10. physicskit/astro/tests/conftest.py +7 -0
  11. physicskit/astro/tests/test_cosmic_web.py +159 -0
  12. physicskit/astro/tests/test_galactic_dynamics.py +46 -0
  13. physicskit/astro/tests/test_nbody.py +81 -0
  14. physicskit/astro/tests/test_orbital_mechanics.py +73 -0
  15. physicskit/astro/tests/test_stellar_dynamo.py +198 -0
  16. physicskit/astro/tests/test_stellar_structure.py +52 -0
  17. physicskit/astro/tests/test_visualizers.py +159 -0
  18. physicskit/astro/visualizers.py +628 -0
  19. physicskit/chaos/__init__.py +55 -0
  20. physicskit/chaos/core/__init__.py +3 -0
  21. physicskit/chaos/core/base_system.py +342 -0
  22. physicskit/chaos/core/integrators.py +40 -0
  23. physicskit/chaos/exceptions.py +24 -0
  24. physicskit/chaos/quantum/__init__.py +38 -0
  25. physicskit/chaos/quantum/billiards.py +274 -0
  26. physicskit/chaos/quantum/husimi.py +85 -0
  27. physicskit/chaos/quantum/maps.py +366 -0
  28. physicskit/chaos/systems/__init__.py +38 -0
  29. physicskit/chaos/systems/billiards.py +1131 -0
  30. physicskit/chaos/systems/continuous.py +1276 -0
  31. physicskit/chaos/systems/maps.py +557 -0
  32. physicskit/chaos/tests/__init__.py +0 -0
  33. physicskit/chaos/tests/test_basins.py +76 -0
  34. physicskit/chaos/tests/test_bifurcation.py +132 -0
  35. physicskit/chaos/tests/test_billiards.py +115 -0
  36. physicskit/chaos/tests/test_continuous.py +224 -0
  37. physicskit/chaos/tests/test_dimension.py +69 -0
  38. physicskit/chaos/tests/test_divergence.py +90 -0
  39. physicskit/chaos/tests/test_dynamic_plots.py +269 -0
  40. physicskit/chaos/tests/test_integrators.py +96 -0
  41. physicskit/chaos/tests/test_io.py +41 -0
  42. physicskit/chaos/tests/test_maps.py +110 -0
  43. physicskit/chaos/tests/test_metrics.py +115 -0
  44. physicskit/chaos/tests/test_phase_space.py +73 -0
  45. physicskit/chaos/tests/test_properties.py +116 -0
  46. physicskit/chaos/tests/test_quantum_billiards.py +94 -0
  47. physicskit/chaos/tests/test_quantum_maps.py +144 -0
  48. physicskit/chaos/tests/test_recurrence.py +51 -0
  49. physicskit/chaos/tests/test_repr_and_api_consistency.py +134 -0
  50. physicskit/chaos/tests/test_section.py +49 -0
  51. physicskit/chaos/tests/test_spectral.py +48 -0
  52. physicskit/chaos/tests/test_streamlit_app.py +45 -0
  53. physicskit/chaos/tests/test_timeseries.py +131 -0
  54. physicskit/chaos/tests/test_viewer3d.py +41 -0
  55. physicskit/chaos/utils/__init__.py +46 -0
  56. physicskit/chaos/utils/dimension.py +139 -0
  57. physicskit/chaos/utils/io.py +95 -0
  58. physicskit/chaos/utils/metrics.py +304 -0
  59. physicskit/chaos/utils/recurrence.py +122 -0
  60. physicskit/chaos/utils/spectral.py +69 -0
  61. physicskit/chaos/utils/timeseries.py +303 -0
  62. physicskit/chaos/visualizers/__init__.py +85 -0
  63. physicskit/chaos/visualizers/basins.py +185 -0
  64. physicskit/chaos/visualizers/bifurcation.py +273 -0
  65. physicskit/chaos/visualizers/divergence.py +374 -0
  66. physicskit/chaos/visualizers/dynamic_plots.py +1372 -0
  67. physicskit/chaos/visualizers/phase_space.py +133 -0
  68. physicskit/chaos/visualizers/quantum.py +299 -0
  69. physicskit/chaos/visualizers/recurrence.py +91 -0
  70. physicskit/chaos/visualizers/section.py +161 -0
  71. physicskit/chaos/visualizers/theme.py +31 -0
  72. physicskit/chaos/visualizers/viewer3d.py +82 -0
  73. physicskit/classical/__init__.py +55 -0
  74. physicskit/classical/core/__init__.py +17 -0
  75. physicskit/classical/core/base_system.py +510 -0
  76. physicskit/classical/core/integrators.py +397 -0
  77. physicskit/classical/systems/__init__.py +32 -0
  78. physicskit/classical/systems/chains.py +334 -0
  79. physicskit/classical/systems/hamiltonian.py +215 -0
  80. physicskit/classical/systems/lagrangian.py +308 -0
  81. physicskit/classical/systems/newtonian.py +536 -0
  82. physicskit/classical/systems/rotations.py +614 -0
  83. physicskit/classical/tests/__init__.py +0 -0
  84. physicskit/classical/tests/conftest.py +7 -0
  85. physicskit/classical/tests/test_base_system.py +113 -0
  86. physicskit/classical/tests/test_chains.py +84 -0
  87. physicskit/classical/tests/test_conservation.py +244 -0
  88. physicskit/classical/tests/test_conservation_utils.py +47 -0
  89. physicskit/classical/tests/test_hamiltonian.py +44 -0
  90. physicskit/classical/tests/test_integrators.py +123 -0
  91. physicskit/classical/tests/test_lagrangian.py +69 -0
  92. physicskit/classical/tests/test_newtonian.py +45 -0
  93. physicskit/classical/tests/test_rotations.py +160 -0
  94. physicskit/classical/tests/test_stepsize.py +149 -0
  95. physicskit/classical/tests/test_symbolic.py +125 -0
  96. physicskit/classical/tests/test_visualizers.py +172 -0
  97. physicskit/classical/utils/__init__.py +20 -0
  98. physicskit/classical/utils/conservation.py +126 -0
  99. physicskit/classical/utils/stepsize.py +187 -0
  100. physicskit/classical/utils/symbolic.py +235 -0
  101. physicskit/classical/visualizers/__init__.py +25 -0
  102. physicskit/classical/visualizers/animations.py +508 -0
  103. physicskit/classical/visualizers/interactive.py +113 -0
  104. physicskit/classical/visualizers/modal_analysis.py +97 -0
  105. physicskit/classical/visualizers/phase_space.py +214 -0
  106. physicskit/condensed/__init__.py +122 -0
  107. physicskit/condensed/anderson_localization.py +155 -0
  108. physicskit/condensed/correlated.py +284 -0
  109. physicskit/condensed/ginzburg_landau.py +220 -0
  110. physicskit/condensed/landau_levels.py +246 -0
  111. physicskit/condensed/laughlin.py +229 -0
  112. physicskit/condensed/models.py +627 -0
  113. physicskit/condensed/tests/__init__.py +0 -0
  114. physicskit/condensed/tests/test_condensed.py +518 -0
  115. physicskit/condensed/tests/test_visualizers.py +87 -0
  116. physicskit/condensed/tight_binding.py +445 -0
  117. physicskit/condensed/topological_insulator_3d.py +178 -0
  118. physicskit/condensed/topology.py +217 -0
  119. physicskit/condensed/visualizers.py +296 -0
  120. physicskit/condensed/weyl.py +180 -0
  121. physicskit/constants.py +184 -0
  122. physicskit/fields/__init__.py +111 -0
  123. physicskit/fields/electrodynamics.py +671 -0
  124. physicskit/fields/quantum_fields.py +510 -0
  125. physicskit/fields/solitons.py +527 -0
  126. physicskit/fields/tests/__init__.py +0 -0
  127. physicskit/fields/tests/conftest.py +7 -0
  128. physicskit/fields/tests/test_field_animations.py +154 -0
  129. physicskit/fields/tests/test_fields.py +439 -0
  130. physicskit/fields/tests/test_visualizers.py +101 -0
  131. physicskit/fields/visualizers.py +503 -0
  132. physicskit/fluids/__init__.py +134 -0
  133. physicskit/fluids/core/__init__.py +23 -0
  134. physicskit/fluids/core/grid.py +165 -0
  135. physicskit/fluids/core/timestepping.py +231 -0
  136. physicskit/fluids/exceptions.py +26 -0
  137. physicskit/fluids/systems/__init__.py +70 -0
  138. physicskit/fluids/systems/compressible_flow.py +281 -0
  139. physicskit/fluids/systems/instabilities.py +343 -0
  140. physicskit/fluids/systems/navier_stokes.py +221 -0
  141. physicskit/fluids/systems/potential_flow.py +440 -0
  142. physicskit/fluids/systems/viscous_flow.py +425 -0
  143. physicskit/fluids/systems/vortex_dynamics.py +304 -0
  144. physicskit/fluids/tests/__init__.py +0 -0
  145. physicskit/fluids/tests/conftest.py +7 -0
  146. physicskit/fluids/tests/test_compressible_flow.py +66 -0
  147. physicskit/fluids/tests/test_instabilities.py +115 -0
  148. physicskit/fluids/tests/test_navier_stokes.py +67 -0
  149. physicskit/fluids/tests/test_potential_flow.py +92 -0
  150. physicskit/fluids/tests/test_static_visualizers.py +91 -0
  151. physicskit/fluids/tests/test_utils.py +100 -0
  152. physicskit/fluids/tests/test_viscous_flow.py +110 -0
  153. physicskit/fluids/tests/test_visualizers.py +34 -0
  154. physicskit/fluids/tests/test_vortex_dynamics.py +88 -0
  155. physicskit/fluids/utils/__init__.py +18 -0
  156. physicskit/fluids/utils/dimensionless.py +239 -0
  157. physicskit/fluids/utils/spectral_analysis.py +142 -0
  158. physicskit/fluids/visualizers/__init__.py +14 -0
  159. physicskit/fluids/visualizers/compressible.py +39 -0
  160. physicskit/fluids/visualizers/flow_fields.py +252 -0
  161. physicskit/fluids/visualizers/potential_flow.py +51 -0
  162. physicskit/fluids/visualizers/spectra.py +62 -0
  163. physicskit/fluids/visualizers/theme.py +38 -0
  164. physicskit/integrators/__init__.py +52 -0
  165. physicskit/integrators/adaptive.py +199 -0
  166. physicskit/integrators/fixed_step.py +360 -0
  167. physicskit/integrators/tests/__init__.py +0 -0
  168. physicskit/integrators/tests/test_integrators.py +101 -0
  169. physicskit/optics/__init__.py +124 -0
  170. physicskit/optics/_compat.py +5 -0
  171. physicskit/optics/gaussian.py +382 -0
  172. physicskit/optics/quantum_optics.py +428 -0
  173. physicskit/optics/ray.py +469 -0
  174. physicskit/optics/tests/__init__.py +0 -0
  175. physicskit/optics/tests/test_diffraction_animation.py +38 -0
  176. physicskit/optics/tests/test_gaussian.py +170 -0
  177. physicskit/optics/tests/test_quantum_optics.py +147 -0
  178. physicskit/optics/tests/test_ray.py +177 -0
  179. physicskit/optics/tests/test_visualizers.py +58 -0
  180. physicskit/optics/tests/test_wave.py +146 -0
  181. physicskit/optics/visualizers.py +264 -0
  182. physicskit/optics/wave.py +365 -0
  183. physicskit/particle/__init__.py +152 -0
  184. physicskit/particle/collider.py +382 -0
  185. physicskit/particle/confinement.py +125 -0
  186. physicskit/particle/decays.py +357 -0
  187. physicskit/particle/electroweak.py +300 -0
  188. physicskit/particle/kinematics.py +271 -0
  189. physicskit/particle/neutrinos.py +84 -0
  190. physicskit/particle/nuclear.py +100 -0
  191. physicskit/particle/scattering.py +159 -0
  192. physicskit/particle/tests/__init__.py +0 -0
  193. physicskit/particle/tests/conftest.py +7 -0
  194. physicskit/particle/tests/test_collider.py +119 -0
  195. physicskit/particle/tests/test_confinement.py +42 -0
  196. physicskit/particle/tests/test_decays.py +112 -0
  197. physicskit/particle/tests/test_electroweak.py +97 -0
  198. physicskit/particle/tests/test_kinematics.py +138 -0
  199. physicskit/particle/tests/test_neutrinos.py +36 -0
  200. physicskit/particle/tests/test_nuclear.py +45 -0
  201. physicskit/particle/tests/test_scattering.py +64 -0
  202. physicskit/particle/tests/test_visualizers.py +214 -0
  203. physicskit/particle/visualizers/__init__.py +38 -0
  204. physicskit/particle/visualizers/animations.py +705 -0
  205. physicskit/particle/visualizers/static.py +101 -0
  206. physicskit/plasma/__init__.py +176 -0
  207. physicskit/plasma/acceleration.py +152 -0
  208. physicskit/plasma/instabilities.py +417 -0
  209. physicskit/plasma/kinetic.py +588 -0
  210. physicskit/plasma/mhd.py +463 -0
  211. physicskit/plasma/single_particle.py +562 -0
  212. physicskit/plasma/tests/__init__.py +0 -0
  213. physicskit/plasma/tests/conftest.py +7 -0
  214. physicskit/plasma/tests/test_new_time_domain_features.py +222 -0
  215. physicskit/plasma/tests/test_plasma.py +232 -0
  216. physicskit/plasma/tests/test_visualizers.py +79 -0
  217. physicskit/plasma/turbulence.py +219 -0
  218. physicskit/plasma/visualizers.py +868 -0
  219. physicskit/plasma/waves.py +549 -0
  220. physicskit/quantum/__init__.py +180 -0
  221. physicskit/quantum/_compat.py +20 -0
  222. physicskit/quantum/chapters/__init__.py +0 -0
  223. physicskit/quantum/chapters/entanglement.py +489 -0
  224. physicskit/quantum/chapters/harmonic_spin.py +517 -0
  225. physicskit/quantum/chapters/hydrogen_am.py +292 -0
  226. physicskit/quantum/chapters/perturbation.py +350 -0
  227. physicskit/quantum/chapters/potentials.py +997 -0
  228. physicskit/quantum/chapters/spin.py +306 -0
  229. physicskit/quantum/chapters/wave_packets.py +541 -0
  230. physicskit/quantum/core/__init__.py +0 -0
  231. physicskit/quantum/core/eigensolvers.py +311 -0
  232. physicskit/quantum/core/operators.py +296 -0
  233. physicskit/quantum/core/solvers.py +271 -0
  234. physicskit/quantum/tests/__init__.py +0 -0
  235. physicskit/quantum/tests/conftest.py +7 -0
  236. physicskit/quantum/tests/test_animations.py +225 -0
  237. physicskit/quantum/tests/test_compat.py +43 -0
  238. physicskit/quantum/tests/test_eigensolvers.py +24 -0
  239. physicskit/quantum/tests/test_entanglement.py +127 -0
  240. physicskit/quantum/tests/test_harmonic_spin.py +140 -0
  241. physicskit/quantum/tests/test_hydrogen_am.py +48 -0
  242. physicskit/quantum/tests/test_measure.py +110 -0
  243. physicskit/quantum/tests/test_operators.py +105 -0
  244. physicskit/quantum/tests/test_perturbation.py +112 -0
  245. physicskit/quantum/tests/test_physics_checks.py +131 -0
  246. physicskit/quantum/tests/test_potentials.py +191 -0
  247. physicskit/quantum/tests/test_solvers.py +70 -0
  248. physicskit/quantum/tests/test_spin.py +22 -0
  249. physicskit/quantum/tests/test_unitarity.py +84 -0
  250. physicskit/quantum/tests/test_wave_packets.py +53 -0
  251. physicskit/quantum/utils/__init__.py +0 -0
  252. physicskit/quantum/utils/measure.py +253 -0
  253. physicskit/quantum/visualizers/__init__.py +0 -0
  254. physicskit/quantum/visualizers/bloch_sphere.py +205 -0
  255. physicskit/quantum/visualizers/entanglement.py +58 -0
  256. physicskit/quantum/visualizers/orbitals.py +187 -0
  257. physicskit/quantum/visualizers/phase_space.py +179 -0
  258. physicskit/quantum/visualizers/wavefunctions.py +230 -0
  259. physicskit/relativity/__init__.py +61 -0
  260. physicskit/relativity/chapters/__init__.py +33 -0
  261. physicskit/relativity/chapters/cosmology.py +190 -0
  262. physicskit/relativity/chapters/gw_merger.py +399 -0
  263. physicskit/relativity/chapters/kerr.py +371 -0
  264. physicskit/relativity/chapters/lensing.py +196 -0
  265. physicskit/relativity/chapters/neutron_star.py +149 -0
  266. physicskit/relativity/chapters/schwarzschild.py +412 -0
  267. physicskit/relativity/chapters/timekeeping.py +133 -0
  268. physicskit/relativity/core/__init__.py +12 -0
  269. physicskit/relativity/core/geodesics.py +216 -0
  270. physicskit/relativity/core/kerr_raytracer.py +259 -0
  271. physicskit/relativity/core/raytracer.py +241 -0
  272. physicskit/relativity/core/tensors.py +468 -0
  273. physicskit/relativity/tests/__init__.py +0 -0
  274. physicskit/relativity/tests/test_conservations.py +54 -0
  275. physicskit/relativity/tests/test_constants.py +34 -0
  276. physicskit/relativity/tests/test_cosmology.py +61 -0
  277. physicskit/relativity/tests/test_gw_merger.py +117 -0
  278. physicskit/relativity/tests/test_kerr.py +116 -0
  279. physicskit/relativity/tests/test_kerr_raytracer.py +103 -0
  280. physicskit/relativity/tests/test_lensing.py +75 -0
  281. physicskit/relativity/tests/test_neutron_star.py +48 -0
  282. physicskit/relativity/tests/test_raytracer.py +67 -0
  283. physicskit/relativity/tests/test_schwarzschild.py +126 -0
  284. physicskit/relativity/tests/test_shadow_render.py +81 -0
  285. physicskit/relativity/tests/test_tensors.py +86 -0
  286. physicskit/relativity/tests/test_timekeeping.py +51 -0
  287. physicskit/relativity/tests/test_visualizers_misc.py +115 -0
  288. physicskit/relativity/utils/__init__.py +1 -0
  289. physicskit/relativity/utils/constants.py +232 -0
  290. physicskit/relativity/visualizers/__init__.py +46 -0
  291. physicskit/relativity/visualizers/interactive.py +127 -0
  292. physicskit/relativity/visualizers/shadow_render.py +367 -0
  293. physicskit/relativity/visualizers/spacetime_3d.py +92 -0
  294. physicskit/relativity/visualizers/spacetime_diagrams.py +198 -0
  295. physicskit/relativity/visualizers/wave_plots.py +185 -0
  296. physicskit/rmt/__init__.py +8 -0
  297. physicskit/rmt/ensembles/__init__.py +108 -0
  298. physicskit/rmt/ensembles/banded.py +114 -0
  299. physicskit/rmt/ensembles/base.py +135 -0
  300. physicskit/rmt/ensembles/bdg.py +218 -0
  301. physicskit/rmt/ensembles/chiral.py +308 -0
  302. physicskit/rmt/ensembles/circular.py +104 -0
  303. physicskit/rmt/ensembles/crossover.py +82 -0
  304. physicskit/rmt/ensembles/density_matrix.py +148 -0
  305. physicskit/rmt/ensembles/effective_hamiltonian.py +230 -0
  306. physicskit/rmt/ensembles/embedded.py +207 -0
  307. physicskit/rmt/ensembles/gaussian.py +153 -0
  308. physicskit/rmt/ensembles/ginibre.py +110 -0
  309. physicskit/rmt/ensembles/girko.py +190 -0
  310. physicskit/rmt/ensembles/graph_laplacian.py +92 -0
  311. physicskit/rmt/ensembles/haar.py +57 -0
  312. physicskit/rmt/ensembles/jacobi.py +182 -0
  313. physicskit/rmt/ensembles/poisson.py +77 -0
  314. physicskit/rmt/ensembles/polynomial.py +139 -0
  315. physicskit/rmt/ensembles/pt_symmetric.py +120 -0
  316. physicskit/rmt/ensembles/single_ring.py +170 -0
  317. physicskit/rmt/ensembles/sparse.py +95 -0
  318. physicskit/rmt/ensembles/syk.py +170 -0
  319. physicskit/rmt/ensembles/truncated_unitary.py +80 -0
  320. physicskit/rmt/ensembles/universality.py +92 -0
  321. physicskit/rmt/ensembles/wishart.py +88 -0
  322. physicskit/rmt/py.typed +0 -0
  323. physicskit/rmt/spectrum.py +82 -0
  324. physicskit/rmt/stats/__init__.py +160 -0
  325. physicskit/rmt/stats/characteristic_polynomial.py +77 -0
  326. physicskit/rmt/stats/circular_law.py +54 -0
  327. physicskit/rmt/stats/correlations.py +162 -0
  328. physicskit/rmt/stats/density.py +70 -0
  329. physicskit/rmt/stats/entanglement.py +36 -0
  330. physicskit/rmt/stats/free_probability.py +202 -0
  331. physicskit/rmt/stats/localization.py +338 -0
  332. physicskit/rmt/stats/marchenko_pastur.py +55 -0
  333. physicskit/rmt/stats/pt_symmetric.py +265 -0
  334. physicskit/rmt/stats/ratios.py +110 -0
  335. physicskit/rmt/stats/real_ginibre.py +134 -0
  336. physicskit/rmt/stats/rigidity.py +272 -0
  337. physicskit/rmt/stats/single_ring.py +95 -0
  338. physicskit/rmt/stats/spacing.py +150 -0
  339. physicskit/rmt/stats/tracy_widom.py +175 -0
  340. physicskit/rmt/stats/truncated_unitary.py +40 -0
  341. physicskit/rmt/stats/unfolding.py +37 -0
  342. physicskit/rmt/stats/wachter.py +67 -0
  343. physicskit/rmt/tests/__init__.py +0 -0
  344. physicskit/rmt/tests/cache_utils.py +107 -0
  345. physicskit/rmt/tests/conftest.py +8 -0
  346. physicskit/rmt/tests/test_banded.py +86 -0
  347. physicskit/rmt/tests/test_base.py +29 -0
  348. physicskit/rmt/tests/test_bdg.py +133 -0
  349. physicskit/rmt/tests/test_characteristic_polynomial.py +50 -0
  350. physicskit/rmt/tests/test_chiral.py +273 -0
  351. physicskit/rmt/tests/test_circular.py +225 -0
  352. physicskit/rmt/tests/test_crossover.py +72 -0
  353. physicskit/rmt/tests/test_density.py +23 -0
  354. physicskit/rmt/tests/test_density_matrix.py +181 -0
  355. physicskit/rmt/tests/test_effective_hamiltonian.py +140 -0
  356. physicskit/rmt/tests/test_embedded.py +169 -0
  357. physicskit/rmt/tests/test_free_probability.py +81 -0
  358. physicskit/rmt/tests/test_gaussian_ensembles.py +22 -0
  359. physicskit/rmt/tests/test_gaussian_semicircle.py +127 -0
  360. physicskit/rmt/tests/test_ginibre_circular_law.py +131 -0
  361. physicskit/rmt/tests/test_girko.py +178 -0
  362. physicskit/rmt/tests/test_graph_laplacian.py +102 -0
  363. physicskit/rmt/tests/test_haar.py +118 -0
  364. physicskit/rmt/tests/test_jacobi_wachter.py +136 -0
  365. physicskit/rmt/tests/test_localization.py +240 -0
  366. physicskit/rmt/tests/test_poisson.py +149 -0
  367. physicskit/rmt/tests/test_polynomial.py +107 -0
  368. physicskit/rmt/tests/test_pt_symmetric.py +178 -0
  369. physicskit/rmt/tests/test_random_state.py +19 -0
  370. physicskit/rmt/tests/test_real_ginibre.py +112 -0
  371. physicskit/rmt/tests/test_rigidity_universality.py +167 -0
  372. physicskit/rmt/tests/test_single_ring.py +132 -0
  373. physicskit/rmt/tests/test_spacing_and_ratios.py +122 -0
  374. physicskit/rmt/tests/test_sparse.py +71 -0
  375. physicskit/rmt/tests/test_spectrum.py +27 -0
  376. physicskit/rmt/tests/test_syk.py +136 -0
  377. physicskit/rmt/tests/test_tracy_widom.py +113 -0
  378. physicskit/rmt/tests/test_tridiagonal.py +41 -0
  379. physicskit/rmt/tests/test_truncated_unitary.py +80 -0
  380. physicskit/rmt/tests/test_validation_base.py +25 -0
  381. physicskit/rmt/tests/test_validation_misc.py +67 -0
  382. physicskit/rmt/tests/test_wishart_marchenko_pastur.py +120 -0
  383. physicskit/rmt/utils/__init__.py +0 -0
  384. physicskit/rmt/utils/haar.py +128 -0
  385. physicskit/rmt/utils/random_state.py +24 -0
  386. physicskit/rmt/utils/tridiagonal.py +167 -0
  387. physicskit/rmt/validation/__init__.py +36 -0
  388. physicskit/rmt/validation/base.py +126 -0
  389. physicskit/rmt/validation/circular_law.py +80 -0
  390. physicskit/rmt/validation/marchenko_pastur.py +53 -0
  391. physicskit/rmt/validation/ratio_distribution.py +53 -0
  392. physicskit/rmt/validation/real_ginibre.py +60 -0
  393. physicskit/rmt/validation/sine_kernel.py +57 -0
  394. physicskit/rmt/validation/single_ring.py +68 -0
  395. physicskit/rmt/validation/tracy_widom.py +89 -0
  396. physicskit/rmt/validation/universality.py +100 -0
  397. physicskit/rmt/validation/wachter.py +52 -0
  398. physicskit/rmt/validation/wigner_semicircle.py +40 -0
  399. physicskit/rmt/validation/wigner_surmise.py +58 -0
  400. physicskit/semiclassical/__init__.py +94 -0
  401. physicskit/semiclassical/_compat.py +5 -0
  402. physicskit/semiclassical/core/__init__.py +41 -0
  403. physicskit/semiclassical/core/gutzwiller.py +215 -0
  404. physicskit/semiclassical/core/path_integral.py +594 -0
  405. physicskit/semiclassical/core/propagators.py +579 -0
  406. physicskit/semiclassical/core/wkb.py +308 -0
  407. physicskit/semiclassical/systems/__init__.py +13 -0
  408. physicskit/semiclassical/systems/scarring.py +278 -0
  409. physicskit/semiclassical/tests/__init__.py +0 -0
  410. physicskit/semiclassical/tests/test_gutzwiller.py +35 -0
  411. physicskit/semiclassical/tests/test_path_integral.py +194 -0
  412. physicskit/semiclassical/tests/test_propagators.py +82 -0
  413. physicskit/semiclassical/tests/test_scarring.py +43 -0
  414. physicskit/semiclassical/tests/test_visualizers.py +89 -0
  415. physicskit/semiclassical/tests/test_wkb.py +30 -0
  416. physicskit/semiclassical/visualizers/__init__.py +17 -0
  417. physicskit/semiclassical/visualizers/gutzwiller.py +55 -0
  418. physicskit/semiclassical/visualizers/path_integral.py +195 -0
  419. physicskit/semiclassical/visualizers/propagators.py +89 -0
  420. physicskit/semiclassical/visualizers/scarring.py +146 -0
  421. physicskit/semiclassical/visualizers/wkb.py +55 -0
  422. physicskit/statphys/__init__.py +80 -0
  423. physicskit/statphys/chapters/__init__.py +43 -0
  424. physicskit/statphys/chapters/ehrenfest_urn.py +112 -0
  425. physicskit/statphys/chapters/ising_lattice.py +431 -0
  426. physicskit/statphys/chapters/kpz_growth.py +129 -0
  427. physicskit/statphys/chapters/molecular_dynamics.py +258 -0
  428. physicskit/statphys/chapters/nonequilibrium_work.py +146 -0
  429. physicskit/statphys/chapters/percolation.py +305 -0
  430. physicskit/statphys/chapters/random_walk.py +142 -0
  431. physicskit/statphys/chapters/renormalization.py +148 -0
  432. physicskit/statphys/chapters/sandpile.py +109 -0
  433. physicskit/statphys/chapters/spin_glass.py +275 -0
  434. physicskit/statphys/core/__init__.py +10 -0
  435. physicskit/statphys/core/kpz_engine.py +53 -0
  436. physicskit/statphys/core/md_engine.py +157 -0
  437. physicskit/statphys/core/monte_carlo.py +462 -0
  438. physicskit/statphys/core/random_walk_engine.py +68 -0
  439. physicskit/statphys/core/sandpile_engine.py +52 -0
  440. physicskit/statphys/tests/__init__.py +0 -0
  441. physicskit/statphys/tests/test_dynamics.py +66 -0
  442. physicskit/statphys/tests/test_ehrenfest_urn.py +44 -0
  443. physicskit/statphys/tests/test_finite_size_scaling.py +62 -0
  444. physicskit/statphys/tests/test_interactive.py +33 -0
  445. physicskit/statphys/tests/test_ising.py +68 -0
  446. physicskit/statphys/tests/test_kpz.py +55 -0
  447. physicskit/statphys/tests/test_landau_theory.py +49 -0
  448. physicskit/statphys/tests/test_md_engine.py +31 -0
  449. physicskit/statphys/tests/test_molecular_dynamics.py +72 -0
  450. physicskit/statphys/tests/test_nonequilibrium_work.py +33 -0
  451. physicskit/statphys/tests/test_partition_function.py +26 -0
  452. physicskit/statphys/tests/test_percolation.py +116 -0
  453. physicskit/statphys/tests/test_potts_model.py +46 -0
  454. physicskit/statphys/tests/test_random_walk.py +50 -0
  455. physicskit/statphys/tests/test_renormalization.py +44 -0
  456. physicskit/statphys/tests/test_sandpile.py +44 -0
  457. physicskit/statphys/tests/test_spin_glass.py +73 -0
  458. physicskit/statphys/tests/test_thermodynamics.py +121 -0
  459. physicskit/statphys/tests/test_xy_model.py +45 -0
  460. physicskit/statphys/utils/__init__.py +1 -0
  461. physicskit/statphys/utils/dynamics.py +100 -0
  462. physicskit/statphys/utils/finite_size_scaling.py +171 -0
  463. physicskit/statphys/utils/landau_theory.py +151 -0
  464. physicskit/statphys/utils/partition_function.py +109 -0
  465. physicskit/statphys/utils/thermodynamics.py +356 -0
  466. physicskit/statphys/visualizers/__init__.py +87 -0
  467. physicskit/statphys/visualizers/interactive.py +161 -0
  468. physicskit/statphys/visualizers/jarzynski_render.py +44 -0
  469. physicskit/statphys/visualizers/kpz_render.py +65 -0
  470. physicskit/statphys/visualizers/lattice_render.py +212 -0
  471. physicskit/statphys/visualizers/particle_render.py +121 -0
  472. physicskit/statphys/visualizers/random_walk_render.py +106 -0
  473. physicskit/statphys/visualizers/rg_render.py +45 -0
  474. physicskit/statphys/visualizers/sandpile_render.py +65 -0
  475. physicskit/statphys/visualizers/spin_glass_render.py +40 -0
  476. physicskit/statphys/visualizers/urn_render.py +38 -0
  477. physicskit/statphys/visualizers/vortex_render.py +84 -0
  478. physicskit/tests/__init__.py +0 -0
  479. physicskit/tests/test_constants.py +38 -0
  480. physicskit-0.1.0.dist-info/METADATA +196 -0
  481. physicskit-0.1.0.dist-info/RECORD +484 -0
  482. physicskit-0.1.0.dist-info/WHEEL +5 -0
  483. physicskit-0.1.0.dist-info/licenses/LICENSE +21 -0
  484. physicskit-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,304 @@
1
+ """Metrics for quantifying chaos: Lyapunov exponents, energy drift, phase-volume expansion."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+
7
+ import numpy as np
8
+ from numpy.typing import NDArray
9
+
10
+ from physicskit.chaos.core.base_system import DiscreteMap, DynamicalSystem
11
+
12
+
13
+ def lyapunov_exponent_from_divergence(t: NDArray[np.float64], delta: NDArray[np.float64]) -> float:
14
+ """Estimate the largest Lyapunov exponent from a trajectory-separation curve.
15
+
16
+ The estimate is the slope of ``ln(delta)`` vs. `t`, fit by ordinary least
17
+ squares.
18
+
19
+ Parameters
20
+ ----------
21
+ t : ndarray of float, shape (n,)
22
+ Time points.
23
+ delta : ndarray of float, shape (n,)
24
+ The (positive) separation between two initially nearby trajectories
25
+ at each time in `t`, e.g. as produced by
26
+ :func:`physicskit.chaos.visualizers.divergence.trajectory_divergence`.
27
+
28
+ Returns
29
+ -------
30
+ float
31
+ Estimated largest Lyapunov exponent ``lambda_max``.
32
+ """
33
+ t = np.asarray(t, dtype=np.float64)
34
+ delta = np.asarray(delta, dtype=np.float64)
35
+ mask = delta > 0
36
+ slope, _ = np.polyfit(t[mask], np.log(delta[mask]), 1)
37
+ return float(slope)
38
+
39
+
40
+ def numerical_jacobian(system: DynamicalSystem, state: NDArray[np.float64], t: float, eps: float = 1e-6) -> NDArray[np.float64]:
41
+ """Central-difference Jacobian of a dynamical system's vector field.
42
+
43
+ Parameters
44
+ ----------
45
+ system : DynamicalSystem
46
+ System whose ``rhs(state, t)`` is to be differentiated.
47
+ state : ndarray of float, shape (dim,)
48
+ State at which to evaluate the Jacobian.
49
+ t : float
50
+ Time at which to evaluate the Jacobian.
51
+ eps : float, default 1e-6
52
+ Finite-difference step size.
53
+
54
+ Returns
55
+ -------
56
+ ndarray of float, shape (dim, dim)
57
+ Jacobian matrix ``d(rhs)/d(state)`` at ``(state, t)``.
58
+ """
59
+ state = np.asarray(state, dtype=np.float64)
60
+ dim = state.shape[0]
61
+ jac = np.empty((dim, dim))
62
+ for j in range(dim):
63
+ perturb = np.zeros(dim)
64
+ perturb[j] = eps
65
+ f_plus = system.rhs(state + perturb, t)
66
+ f_minus = system.rhs(state - perturb, t)
67
+ jac[:, j] = (f_plus - f_minus) / (2.0 * eps)
68
+ return jac
69
+
70
+
71
+ def benettin_lyapunov_spectrum(
72
+ system: DynamicalSystem,
73
+ state0: NDArray[np.float64],
74
+ dt: float = 0.01,
75
+ n_steps: int = 5000,
76
+ n_transient: int = 500,
77
+ ) -> NDArray[np.float64]:
78
+ """Estimate the full Lyapunov spectrum via the Benettin QR method.
79
+
80
+ Integrates the reference trajectory and an orthonormal tangent-space
81
+ frame *simultaneously* with matching-order RK4 (the frame's variational
82
+ equation ``dY/dt = J(x(t)) Y`` is evaluated, via the numerical Jacobian,
83
+ at each of RK4's four substages, exactly as the state itself is),
84
+ periodically re-orthonormalizing the frame with QR decomposition and
85
+ accumulating the log-growth of each axis. Integrating the frame at only
86
+ 1st-order (e.g. forward Euler) while the reference trajectory uses RK4
87
+ would bias the smallest-magnitude exponents (whose true value is often
88
+ near zero) by an :math:`O(\\mathrm{dt})` error that does not shrink
89
+ merely by integrating for longer -- only by refining `dt` itself.
90
+
91
+ Parameters
92
+ ----------
93
+ system : DynamicalSystem
94
+ System whose Lyapunov spectrum is to be estimated.
95
+ state0 : array_like of float, shape (dim,)
96
+ Initial state of the reference trajectory.
97
+ dt : float, default 0.01
98
+ Integration step size.
99
+ n_steps : int, default 5000
100
+ Total number of integration steps.
101
+ n_transient : int, default 500
102
+ Number of initial steps discarded before accumulating exponents, to
103
+ let the frame align with the attractor.
104
+
105
+ Returns
106
+ -------
107
+ ndarray of float, shape (dim,)
108
+ Estimated Lyapunov exponents, in descending order.
109
+ """
110
+ state = np.asarray(state0, dtype=np.float64).copy()
111
+ dim = state.shape[0]
112
+ frame = np.eye(dim)
113
+ log_sums = np.zeros(dim)
114
+ t = 0.0
115
+ n_accum = 0
116
+
117
+ for i in range(n_steps):
118
+ k1_x = system.rhs(state, t)
119
+ k1_f = numerical_jacobian(system, state, t) @ frame
120
+
121
+ state_2 = state + 0.5 * dt * k1_x
122
+ t_2 = t + 0.5 * dt
123
+ k2_x = system.rhs(state_2, t_2)
124
+ k2_f = numerical_jacobian(system, state_2, t_2) @ (frame + 0.5 * dt * k1_f)
125
+
126
+ state_3 = state + 0.5 * dt * k2_x
127
+ k3_x = system.rhs(state_3, t_2)
128
+ k3_f = numerical_jacobian(system, state_3, t_2) @ (frame + 0.5 * dt * k2_f)
129
+
130
+ state_4 = state + dt * k3_x
131
+ t_4 = t + dt
132
+ k4_x = system.rhs(state_4, t_4)
133
+ k4_f = numerical_jacobian(system, state_4, t_4) @ (frame + dt * k3_f)
134
+
135
+ state_next = state + (dt / 6.0) * (k1_x + 2 * k2_x + 2 * k3_x + k4_x)
136
+ frame_next = frame + (dt / 6.0) * (k1_f + 2 * k2_f + 2 * k3_f + k4_f)
137
+
138
+ q, r = np.linalg.qr(frame_next)
139
+ signs = np.sign(np.diag(r))
140
+ signs[signs == 0] = 1.0
141
+ q = q * signs
142
+ r = (r.T * signs).T
143
+ frame = q
144
+
145
+ if i >= n_transient:
146
+ log_sums += np.log(np.abs(np.diag(r)))
147
+ n_accum += 1
148
+
149
+ state = state_next
150
+ t += dt
151
+
152
+ elapsed = n_accum * dt
153
+ return log_sums / elapsed
154
+
155
+
156
+ def numerical_jacobian_map(dmap: DiscreteMap, state: NDArray[np.float64], eps: float = 1e-6) -> NDArray[np.float64]:
157
+ """Central-difference Jacobian of a discrete map's step function.
158
+
159
+ Parameters
160
+ ----------
161
+ dmap : DiscreteMap
162
+ Map whose ``step(state)`` is to be differentiated.
163
+ state : ndarray of float, shape (dim,)
164
+ State at which to evaluate the Jacobian.
165
+ eps : float, default 1e-6
166
+ Finite-difference step size.
167
+
168
+ Returns
169
+ -------
170
+ ndarray of float, shape (dim, dim)
171
+ Jacobian matrix ``d(step)/d(state)`` at `state`.
172
+ """
173
+ state = np.asarray(state, dtype=np.float64)
174
+ dim = state.shape[0]
175
+ jac = np.empty((dim, dim))
176
+ for j in range(dim):
177
+ perturb = np.zeros(dim)
178
+ perturb[j] = eps
179
+ f_plus = dmap.step(state + perturb)
180
+ f_minus = dmap.step(state - perturb)
181
+ jac[:, j] = (f_plus - f_minus) / (2.0 * eps)
182
+ return jac
183
+
184
+
185
+ def map_lyapunov_spectrum(
186
+ dmap: DiscreteMap,
187
+ state0: NDArray[np.float64],
188
+ n_iter: int = 5000,
189
+ n_transient: int = 500,
190
+ ) -> NDArray[np.float64]:
191
+ """Estimate the full Lyapunov spectrum of a discrete map via the Benettin QR method.
192
+
193
+ The discrete-map analog of :func:`benettin_lyapunov_spectrum`: co-evolves
194
+ an orthonormal frame under the linearized (numerically-Jacobian'd) map
195
+ alongside the reference orbit, periodically re-orthonormalizing with QR
196
+ decomposition and accumulating the log-growth of each axis. Unlike the
197
+ continuous-flow version, there is no `dt` to divide by -- exponents come
198
+ out directly in units of "per iteration".
199
+
200
+ Parameters
201
+ ----------
202
+ dmap : DiscreteMap
203
+ Map whose Lyapunov spectrum is to be estimated.
204
+ state0 : array_like of float, shape (dim,)
205
+ Initial state of the reference orbit.
206
+ n_iter : int, default 5000
207
+ Total number of map iterations.
208
+ n_transient : int, default 500
209
+ Number of initial iterations discarded before accumulating
210
+ exponents, to let the frame align with the attractor.
211
+
212
+ Returns
213
+ -------
214
+ ndarray of float, shape (dim,)
215
+ Estimated Lyapunov exponents, in descending order, in units of "per
216
+ iteration".
217
+ """
218
+ state = np.asarray(state0, dtype=np.float64).copy()
219
+ dim = state.shape[0]
220
+ frame = np.eye(dim)
221
+ log_sums = np.zeros(dim)
222
+ n_accum = 0
223
+
224
+ for i in range(n_iter):
225
+ jac = numerical_jacobian_map(dmap, state)
226
+ frame = jac @ frame
227
+ state = dmap.step(state)
228
+
229
+ q, r = np.linalg.qr(frame)
230
+ signs = np.sign(np.diag(r))
231
+ signs[signs == 0] = 1.0
232
+ q = q * signs
233
+ r = (r.T * signs).T
234
+ frame = q
235
+
236
+ if i >= n_transient:
237
+ log_sums += np.log(np.abs(np.diag(r)))
238
+ n_accum += 1
239
+
240
+ return log_sums / n_accum
241
+
242
+
243
+ def energy_drift(
244
+ times: NDArray[np.float64],
245
+ states: NDArray[np.float64],
246
+ energy_func: Callable[[NDArray[np.float64]], float],
247
+ ) -> NDArray[np.float64]:
248
+ """Relative energy drift along a trajectory.
249
+
250
+ Parameters
251
+ ----------
252
+ times : ndarray of float, shape (n,)
253
+ Time points (unused directly, kept for API symmetry with
254
+ :func:`phase_volume_expansion`).
255
+ states : ndarray of float, shape (n, dim)
256
+ State at each time in `times`.
257
+ energy_func : callable
258
+ Function ``energy_func(state) -> float`` computing the conserved (or
259
+ nominally conserved) quantity for a single state; useful for checking
260
+ integrator fidelity (e.g.
261
+ :meth:`physicskit.chaos.systems.continuous.DoublePendulum.energy`).
262
+
263
+ Returns
264
+ -------
265
+ ndarray of float, shape (n,)
266
+ Relative drift ``(E(t) - E(0)) / |E(0)|`` at each time in `times`
267
+ (using an absolute, not relative, denominator when ``E(0)`` is
268
+ numerically zero).
269
+ """
270
+ energies = np.array([energy_func(states[i]) for i in range(states.shape[0])])
271
+ e0 = energies[0]
272
+ denom = abs(e0) if abs(e0) > 1e-12 else 1.0
273
+ return np.asarray((energies - e0) / denom, dtype=np.float64)
274
+
275
+
276
+ def phase_volume_expansion(system: DynamicalSystem, times: NDArray[np.float64], states: NDArray[np.float64]) -> NDArray[np.float64]:
277
+ """Cumulative log phase-space-volume expansion along a trajectory.
278
+
279
+ Uses Liouville's theorem, ``d(log V)/dt = trace(Jacobian)``, integrated
280
+ with the trapezoidal rule.
281
+
282
+ Parameters
283
+ ----------
284
+ system : DynamicalSystem
285
+ System whose phase-space divergence (``trace`` of the Jacobian) is
286
+ evaluated along the trajectory.
287
+ times : ndarray of float, shape (n,)
288
+ Time points.
289
+ states : ndarray of float, shape (n, dim)
290
+ State at each time in `times`.
291
+
292
+ Returns
293
+ -------
294
+ ndarray of float, shape (n,)
295
+ Cumulative ``log(V(t) / V(0))`` at each time in `times`, starting at
296
+ ``0.0``. Negative values indicate a dissipative (volume-contracting)
297
+ system.
298
+ """
299
+ n = states.shape[0]
300
+ divergence = np.empty(n)
301
+ for i in range(n):
302
+ jac = numerical_jacobian(system, states[i], times[i])
303
+ divergence[i] = np.trace(jac)
304
+ return np.concatenate(([0.0], np.cumsum(0.5 * (divergence[:-1] + divergence[1:]) * np.diff(times))))
@@ -0,0 +1,122 @@
1
+ """Poincare recurrence analysis: return-time statistics and Kac's lemma.
2
+
3
+ The Poincare recurrence theorem guarantees that, for a measure-preserving
4
+ dynamical system, almost every trajectory returns arbitrarily close to its
5
+ starting point infinitely often. Kac's lemma sharpens this: for an ergodic
6
+ system, the *mean* time between successive returns to a region ``A`` equals
7
+ the reciprocal of that region's invariant measure, ``mean_recurrence_time ~=
8
+ 1 / mu(A)``. The functions here compute both sides of that relationship
9
+ numerically from a sampled trajectory.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import numpy as np
15
+ from numpy.typing import ArrayLike, NDArray
16
+
17
+
18
+ def recurrence_matrix(states: ArrayLike, epsilon: float) -> NDArray[np.bool_]:
19
+ """The pairwise epsilon-neighborhood recurrence matrix of a trajectory.
20
+
21
+ Parameters
22
+ ----------
23
+ states : array_like of float, shape (n, dim)
24
+ Trajectory samples.
25
+ epsilon : float
26
+ Neighborhood radius.
27
+
28
+ Returns
29
+ -------
30
+ ndarray of bool, shape (n, n)
31
+ ``R[i, j]`` is ``True`` iff ``states[i]`` and ``states[j]`` are within
32
+ `epsilon` of each other (Euclidean distance). The classic "recurrence
33
+ plot" of nonlinear time series analysis is this matrix rendered as an
34
+ image; see :func:`physicskit.chaos.visualizers.recurrence.plot_recurrence_matrix`.
35
+
36
+ Notes
37
+ -----
38
+ This computes all :math:`O(n^2)` pairwise distances; keep `states` to at
39
+ most a few thousand rows.
40
+ """
41
+ states = np.asarray(states, dtype=np.float64)
42
+ diffs = states[:, np.newaxis, :] - states[np.newaxis, :, :]
43
+ dists = np.sqrt(np.sum(diffs * diffs, axis=-1))
44
+ return dists < epsilon
45
+
46
+
47
+ def recurrence_times(states: ArrayLike, epsilon: float, reference_idx: int = 0, dt: float = 1.0) -> NDArray[np.float64]:
48
+ """Gap times between successive returns of a trajectory to a reference point.
49
+
50
+ Parameters
51
+ ----------
52
+ states : array_like of float, shape (n, dim)
53
+ Trajectory samples.
54
+ epsilon : float
55
+ Neighborhood radius defining a "return".
56
+ reference_idx : int, default 0
57
+ Index into `states` of the reference point to measure returns to.
58
+ dt : float, default 1.0
59
+ Time between consecutive rows of `states`, used to convert the
60
+ (integer) step counts between returns into physical time units.
61
+
62
+ Returns
63
+ -------
64
+ ndarray of float, shape (m,)
65
+ The `m` gap times between successive entries into the
66
+ epsilon-neighborhood of ``states[reference_idx]`` (``m`` is the
67
+ number of *returns* observed minus one; empty if there were fewer
68
+ than two returns).
69
+ """
70
+ states = np.asarray(states, dtype=np.float64)
71
+ reference = states[reference_idx]
72
+ dists = np.sqrt(np.sum((states - reference) ** 2, axis=-1))
73
+ inside = dists < epsilon
74
+ # "Entries" are steps where the trajectory just crossed from outside the
75
+ # ball to inside it -- each one is a single recurrence event, even if the
76
+ # trajectory then lingers nearby for several consecutive samples.
77
+ entries = np.flatnonzero(inside[1:] & ~inside[:-1]) + 1
78
+ return np.diff(entries).astype(np.float64) * dt
79
+
80
+
81
+ def kac_lemma_estimate(states: ArrayLike, epsilon: float, reference_idx: int = 0, dt: float = 1.0) -> tuple[float, float, float]:
82
+ """Numerically validate Kac's lemma on a sampled trajectory.
83
+
84
+ Parameters
85
+ ----------
86
+ states : array_like of float, shape (n, dim)
87
+ Trajectory samples, assumed to come from an ergodic,
88
+ measure-preserving (or already attractor-restricted, invariant-measure
89
+ sampling) system.
90
+ epsilon : float
91
+ Neighborhood radius defining "returns" and the region whose measure
92
+ is estimated.
93
+ reference_idx : int, default 0
94
+ Index into `states` of the reference point.
95
+ dt : float, default 1.0
96
+ Time between consecutive rows of `states`.
97
+
98
+ Returns
99
+ -------
100
+ mean_recurrence_time : float
101
+ The mean of :func:`recurrence_times` for this trajectory.
102
+ measure_estimate : float
103
+ The empirical invariant measure of the epsilon-neighborhood: the
104
+ fraction of all sampled points in `states` lying within `epsilon` of
105
+ ``states[reference_idx]``.
106
+ kac_product : float
107
+ ``mean_recurrence_time * measure_estimate / dt``, which Kac's lemma
108
+ predicts should be close to 1 for a long-enough, ergodic trajectory
109
+ (the ``/dt`` converts the measure, a dimensionless fraction, onto the
110
+ same per-step footing as a recurrence time measured in physical
111
+ time units).
112
+ """
113
+ states = np.asarray(states, dtype=np.float64)
114
+ times = recurrence_times(states, epsilon, reference_idx=reference_idx, dt=dt)
115
+ mean_time = float(np.mean(times)) if times.size else float("nan")
116
+
117
+ reference = states[reference_idx]
118
+ dists = np.sqrt(np.sum((states - reference) ** 2, axis=-1))
119
+ measure = float(np.mean(dists < epsilon))
120
+
121
+ kac_product = mean_time * measure / dt
122
+ return mean_time, measure, kac_product
@@ -0,0 +1,69 @@
1
+ """Spectral diagnostics: power spectrum and autocorrelation.
2
+
3
+ The cheapest, most classical way to distinguish periodic, quasiperiodic, and
4
+ chaotic behavior at a glance: a periodic signal's power spectrum is a forest
5
+ of sharp lines, a quasiperiodic one a handful of incommensurate lines (plus
6
+ their sums/differences), and a chaotic one a broadband continuum. These
7
+ functions work on any scalar time series -- a physicskit.chaos-simulated trajectory
8
+ component or independently-supplied data alike.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import numpy as np
14
+ from numpy.typing import ArrayLike, NDArray
15
+
16
+
17
+ def power_spectrum(series: ArrayLike, dt: float = 1.0) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
18
+ """Compute the power spectrum of a (real-valued) scalar time series.
19
+
20
+ Parameters
21
+ ----------
22
+ series : array_like of float, shape (n,)
23
+ Time series (its mean is removed before transforming, so the
24
+ spectrum reflects only its fluctuations).
25
+ dt : float, default 1.0
26
+ Time between consecutive samples.
27
+
28
+ Returns
29
+ -------
30
+ frequencies : ndarray of float, shape (n // 2 + 1,)
31
+ Non-negative frequencies, in cycles per unit time (i.e. matching
32
+ 1/`dt` units).
33
+ power : ndarray of float, shape (n // 2 + 1,)
34
+ Power at each frequency (``|FFT|^2 / n``).
35
+ """
36
+ series = np.asarray(series, dtype=np.float64)
37
+ series = series - series.mean()
38
+ n = series.shape[0]
39
+ spectrum = np.fft.rfft(series)
40
+ power = (np.abs(spectrum) ** 2) / n
41
+ frequencies = np.fft.rfftfreq(n, d=dt)
42
+ return frequencies, power
43
+
44
+
45
+ def autocorrelation(series: ArrayLike, max_lag: int | None = None) -> NDArray[np.float64]:
46
+ """Compute the (normalized) autocorrelation function of a scalar time series.
47
+
48
+ Parameters
49
+ ----------
50
+ series : array_like of float, shape (n,)
51
+ Time series (its mean is removed before correlating).
52
+ max_lag : int, optional
53
+ Largest lag to compute, in samples; defaults to ``n - 1``.
54
+
55
+ Returns
56
+ -------
57
+ ndarray of float, shape (max_lag + 1,)
58
+ The autocorrelation at lags ``0, 1, ..., max_lag``, normalized so
59
+ that ``result[0] == 1.0``.
60
+ """
61
+ series = np.asarray(series, dtype=np.float64)
62
+ series = series - series.mean()
63
+ n = series.shape[0]
64
+ if max_lag is None:
65
+ max_lag = n - 1
66
+
67
+ full = np.correlate(series, series, mode="full")
68
+ zero_lag = full[full.shape[0] // 2 :]
69
+ return np.asarray(zero_lag[: max_lag + 1] / zero_lag[0], dtype=np.float64)