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,557 @@
1
+ """Discrete chaotic maps: the Standard Map, the Henon Map, the Baker's Map, and the Logistic Map."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+ from numba import njit
7
+ from numpy.typing import NDArray
8
+
9
+ from physicskit.chaos.core.base_system import DiscreteMap
10
+ from physicskit.chaos.exceptions import InvalidParameterError
11
+
12
+ TWO_PI: float = 2.0 * np.pi
13
+
14
+
15
+ @njit(cache=True)
16
+ def _standard_map_step(theta: float, p: float, k: float) -> tuple[float, float]:
17
+ """Single Chirikov-Taylor standard map iteration.
18
+
19
+ Parameters
20
+ ----------
21
+ theta : float
22
+ Current angle, in radians.
23
+ p : float
24
+ Current momentum.
25
+ k : float
26
+ Kick strength.
27
+
28
+ Returns
29
+ -------
30
+ theta_new, p_new : float
31
+ Next angle and momentum, both wrapped into ``[0, 2*pi)``.
32
+ """
33
+ p_new = (p + k * np.sin(theta)) % TWO_PI
34
+ theta_new = (theta + p_new) % TWO_PI
35
+ return theta_new, p_new
36
+
37
+
38
+ @njit(cache=True, nogil=True)
39
+ def _standard_map_trajectory(theta0: float, p0: float, k: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
40
+ """Iterate the standard map ``n_iter`` times from ``(theta0, p0)``.
41
+
42
+ Parameters
43
+ ----------
44
+ theta0 : float
45
+ Initial angle, in radians.
46
+ p0 : float
47
+ Initial momentum.
48
+ k : float
49
+ Kick strength.
50
+ n_iter : int
51
+ Number of iterations.
52
+
53
+ Returns
54
+ -------
55
+ thetas, ps : ndarray of float, shape (n_iter + 1,)
56
+ Angle and momentum at each iteration, including the initial values.
57
+ """
58
+ thetas = np.empty(n_iter + 1)
59
+ ps = np.empty(n_iter + 1)
60
+ thetas[0], ps[0] = theta0, p0
61
+ theta, p = theta0, p0
62
+ for i in range(n_iter):
63
+ theta, p = _standard_map_step(theta, p, k)
64
+ thetas[i + 1] = theta
65
+ ps[i + 1] = p
66
+ return thetas, ps
67
+
68
+
69
+ class StandardMap(DiscreteMap):
70
+ """The Chirikov-Taylor standard map on the ``(theta, p)`` cylinder.
71
+
72
+ ``k=0`` is integrable; chaos onset is around ``k~1``, with global chaos
73
+ for ``k >~ 4-5``.
74
+
75
+ Parameters
76
+ ----------
77
+ k : float, default 1.0
78
+ Kick strength.
79
+
80
+ Attributes
81
+ ----------
82
+ k : float
83
+ Kick strength.
84
+ """
85
+
86
+ #: State dimension, always 2. State is ``(theta, p)``.
87
+ dim = 2
88
+
89
+ def __init__(self, k: float = 1.0):
90
+ self.k = float(k)
91
+
92
+ def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
93
+ """Advance ``(theta, p)`` by one map iteration.
94
+
95
+ Parameters
96
+ ----------
97
+ state : array_like of float, shape (2,)
98
+ Current state ``(theta, p)``.
99
+
100
+ Returns
101
+ -------
102
+ ndarray of float, shape (2,)
103
+ Next state ``(theta, p)``.
104
+ """
105
+ theta, p = _standard_map_step(float(state[0]), float(state[1]), self.k)
106
+ return np.array([theta, p])
107
+
108
+ def initial_state(self) -> NDArray[np.float64]:
109
+ """Default initial condition ``(0.1, 0.1)``.
110
+
111
+ Returns
112
+ -------
113
+ ndarray of float, shape (2,)
114
+ """
115
+ return np.array([0.1, 0.1])
116
+
117
+ def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
118
+ """Iterate the map ``n_iter`` times starting from ``state0``.
119
+
120
+ Parameters
121
+ ----------
122
+ state0 : array_like of float, shape (2,), optional
123
+ Initial state ``(theta, p)``; defaults to :meth:`initial_state`.
124
+ n_iter : int, default 1000
125
+ Number of iterations.
126
+
127
+ Returns
128
+ -------
129
+ ndarray of float, shape (n_iter + 1, 2)
130
+ State at each iteration, including ``state0`` as row 0.
131
+ """
132
+ state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
133
+ thetas, ps = _standard_map_trajectory(float(state0[0]), float(state0[1]), self.k, n_iter)
134
+ return np.column_stack([thetas, ps])
135
+
136
+
137
+ @njit(cache=True)
138
+ def _henon_map_step(x: float, y: float, a: float, b: float) -> tuple[float, float]:
139
+ """Single Henon map iteration.
140
+
141
+ Parameters
142
+ ----------
143
+ x, y : float
144
+ Current state.
145
+ a, b : float
146
+ Map parameters.
147
+
148
+ Returns
149
+ -------
150
+ x_new, y_new : float
151
+ Next state.
152
+ """
153
+ return 1.0 - a * x * x + y, b * x
154
+
155
+
156
+ @njit(cache=True, nogil=True)
157
+ def _henon_map_trajectory(x0: float, y0: float, a: float, b: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
158
+ """Iterate the Henon map ``n_iter`` times from ``(x0, y0)``.
159
+
160
+ Parameters
161
+ ----------
162
+ x0, y0 : float
163
+ Initial state.
164
+ a, b : float
165
+ Map parameters.
166
+ n_iter : int
167
+ Number of iterations.
168
+
169
+ Returns
170
+ -------
171
+ xs, ys : ndarray of float, shape (n_iter + 1,)
172
+ State components at each iteration, including the initial values.
173
+ """
174
+ xs = np.empty(n_iter + 1)
175
+ ys = np.empty(n_iter + 1)
176
+ xs[0], ys[0] = x0, y0
177
+ x, y = x0, y0
178
+ for i in range(n_iter):
179
+ x, y = _henon_map_step(x, y, a, b)
180
+ xs[i + 1] = x
181
+ ys[i + 1] = y
182
+ return xs, ys
183
+
184
+
185
+ class HenonMap(DiscreteMap):
186
+ """The Henon map ``x' = 1 - a*x^2 + y, y' = b*x``.
187
+
188
+ The classic chaotic parameters are ``a=1.4, b=0.3``.
189
+
190
+ Parameters
191
+ ----------
192
+ a : float, default 1.4
193
+ Map parameter.
194
+ b : float, default 0.3
195
+ Map parameter.
196
+
197
+ Attributes
198
+ ----------
199
+ a, b : float
200
+ Map parameters.
201
+ """
202
+
203
+ #: State dimension, always 2. State is ``(x, y)``.
204
+ dim = 2
205
+
206
+ def __init__(self, a: float = 1.4, b: float = 0.3):
207
+ self.a = float(a)
208
+ self.b = float(b)
209
+
210
+ def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
211
+ """Advance ``(x, y)`` by one map iteration.
212
+
213
+ Parameters
214
+ ----------
215
+ state : array_like of float, shape (2,)
216
+ Current state ``(x, y)``.
217
+
218
+ Returns
219
+ -------
220
+ ndarray of float, shape (2,)
221
+ Next state ``(x, y)``.
222
+ """
223
+ x, y = _henon_map_step(float(state[0]), float(state[1]), self.a, self.b)
224
+ return np.array([x, y])
225
+
226
+ def initial_state(self) -> NDArray[np.float64]:
227
+ """Default initial condition ``(0, 0)``.
228
+
229
+ Returns
230
+ -------
231
+ ndarray of float, shape (2,)
232
+ """
233
+ return np.array([0.0, 0.0])
234
+
235
+ def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
236
+ """Iterate the map ``n_iter`` times starting from ``state0``.
237
+
238
+ Parameters
239
+ ----------
240
+ state0 : array_like of float, shape (2,), optional
241
+ Initial state ``(x, y)``; defaults to :meth:`initial_state`.
242
+ n_iter : int, default 1000
243
+ Number of iterations.
244
+
245
+ Returns
246
+ -------
247
+ ndarray of float, shape (n_iter + 1, 2)
248
+ State at each iteration, including ``state0`` as row 0.
249
+ """
250
+ state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
251
+ xs, ys = _henon_map_trajectory(float(state0[0]), float(state0[1]), self.a, self.b, n_iter)
252
+ return np.column_stack([xs, ys])
253
+
254
+
255
+ @njit(cache=True)
256
+ def _bakers_map_step(x: float, y: float, alpha: float) -> tuple[float, float]:
257
+ """Single (generalized) baker's map iteration.
258
+
259
+ Parameters
260
+ ----------
261
+ x, y : float
262
+ Current state, each in ``[0, 1)``.
263
+ alpha : float
264
+ Cut position, in ``(0, 1)``.
265
+
266
+ Returns
267
+ -------
268
+ x_new, y_new : float
269
+ Next state, each in ``[0, 1)``.
270
+ """
271
+ if x < alpha:
272
+ return x / alpha, alpha * y
273
+ return (x - alpha) / (1.0 - alpha), alpha + (1.0 - alpha) * y
274
+
275
+
276
+ @njit(cache=True, nogil=True)
277
+ def _bakers_map_trajectory(x0: float, y0: float, alpha: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
278
+ """Iterate the baker's map ``n_iter`` times from ``(x0, y0)``.
279
+
280
+ Parameters
281
+ ----------
282
+ x0, y0 : float
283
+ Initial state, each in ``[0, 1)``.
284
+ alpha : float
285
+ Cut position, in ``(0, 1)``.
286
+ n_iter : int
287
+ Number of iterations.
288
+
289
+ Returns
290
+ -------
291
+ xs, ys : ndarray of float, shape (n_iter + 1,)
292
+ State components at each iteration, including the initial values.
293
+ """
294
+ xs = np.empty(n_iter + 1)
295
+ ys = np.empty(n_iter + 1)
296
+ xs[0], ys[0] = x0, y0
297
+ x, y = x0, y0
298
+ for i in range(n_iter):
299
+ x, y = _bakers_map_step(x, y, alpha)
300
+ xs[i + 1] = x
301
+ ys[i + 1] = y
302
+ return xs, ys
303
+
304
+
305
+ class BakersMap(DiscreteMap):
306
+ """The (generalized) baker's map on the unit square: the textbook model of chaos.
307
+
308
+ The map cuts the unit square ``[0, 1) x [0, 1)`` at ``x = alpha``, stretches
309
+ each piece horizontally back to unit width (contracting it vertically to
310
+ match), and stacks the two pieces -- literally the "stretch, cut, and
311
+ stack" mechanism used to motivate deterministic chaos:
312
+
313
+ .. math::
314
+
315
+ T(x, y) = \\begin{cases}
316
+ (x / \\alpha,\\ \\alpha y) & 0 \\le x < \\alpha \\\\
317
+ ((x - \\alpha) / (1 - \\alpha),\\ \\alpha + (1 - \\alpha) y) & \\alpha \\le x < 1
318
+ \\end{cases}
319
+
320
+ Because each branch is affine with Jacobian determinant exactly 1, the map
321
+ is *area-preserving* (unlike the dissipative Henon map) while still being
322
+ uniformly hyperbolic, ergodic, and mixing: a single long orbit fills the
323
+ unit square uniformly and densely, and its Lyapunov exponents are known
324
+ exactly in closed form (see :meth:`lyapunov_exponents`), making it the
325
+ standard textbook example for validating numerical chaos estimators.
326
+ ``alpha=0.5`` (the default) recovers the classic symmetric baker's map.
327
+
328
+ Parameters
329
+ ----------
330
+ alpha : float, default 0.5
331
+ Cut position, in ``(0, 1)``; the classic symmetric map has
332
+ ``alpha=0.5``.
333
+
334
+ Attributes
335
+ ----------
336
+ alpha : float
337
+ Cut position.
338
+
339
+ Raises
340
+ ------
341
+ ValueError
342
+ If `alpha` does not satisfy ``0 < alpha < 1``.
343
+
344
+ Notes
345
+ -----
346
+ ``x = 0`` is an exact fixed point of the map for *every* `alpha` (since
347
+ ``0 / alpha = 0``, always in the first branch). Because each branch is
348
+ expanding, floating-point rounding error is amplified every step, and
349
+ for many choices of `alpha` and the initial condition, a long-enough
350
+ orbit eventually rounds to exactly ``0.0`` in floating point and gets
351
+ permanently trapped there -- silently producing a degenerate, physically
352
+ meaningless trajectory rather than raising an error. This typically
353
+ happens within a few hundred to a few thousand iterations (it depends
354
+ sensitively on `alpha` and the initial condition: it may also never
355
+ happen within a given run). Do not rely on :meth:`trajectory` orbits of
356
+ more than a few hundred iterations to be a faithful sample of the
357
+ invariant measure without first checking the orbit hasn't collapsed;
358
+ :class:`~physicskit.chaos.systems.maps.HenonMap` and continuous systems such as
359
+ :class:`~physicskit.chaos.systems.continuous.Lorenz` do not share this failure
360
+ mode and are better suited to long-trajectory statistics.
361
+ """
362
+
363
+ #: State dimension, always 2. State is ``(x, y)``.
364
+ dim = 2
365
+
366
+ def __init__(self, alpha: float = 0.5):
367
+ if not 0.0 < alpha < 1.0:
368
+ raise InvalidParameterError("alpha must satisfy 0 < alpha < 1")
369
+ self.alpha = float(alpha)
370
+
371
+ def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
372
+ """Advance ``(x, y)`` by one map iteration.
373
+
374
+ Parameters
375
+ ----------
376
+ state : array_like of float, shape (2,)
377
+ Current state ``(x, y)``, each in ``[0, 1)``.
378
+
379
+ Returns
380
+ -------
381
+ ndarray of float, shape (2,)
382
+ Next state ``(x, y)``.
383
+ """
384
+ x, y = _bakers_map_step(float(state[0]), float(state[1]), self.alpha)
385
+ return np.array([x, y])
386
+
387
+ def initial_state(self) -> NDArray[np.float64]:
388
+ """Default initial condition ``(0.5, 0.5)``.
389
+
390
+ Returns
391
+ -------
392
+ ndarray of float, shape (2,)
393
+ """
394
+ return np.array([0.5, 0.5])
395
+
396
+ def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
397
+ """Iterate the map ``n_iter`` times starting from ``state0``.
398
+
399
+ Parameters
400
+ ----------
401
+ state0 : array_like of float, shape (2,), optional
402
+ Initial state ``(x, y)``, each in ``[0, 1)``; defaults to
403
+ :meth:`initial_state`.
404
+ n_iter : int, default 1000
405
+ Number of iterations.
406
+
407
+ Returns
408
+ -------
409
+ ndarray of float, shape (n_iter + 1, 2)
410
+ State at each iteration, including ``state0`` as row 0.
411
+ """
412
+ state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
413
+ xs, ys = _bakers_map_trajectory(float(state0[0]), float(state0[1]), self.alpha, n_iter)
414
+ return np.column_stack([xs, ys])
415
+
416
+ def lyapunov_exponents(self) -> tuple[float, float]:
417
+ """Exact Lyapunov exponents of the map, in closed form.
418
+
419
+ The baker's map is piecewise-linear and uniformly hyperbolic: almost
420
+ every orbit spends a fraction `alpha` of its time in the expanding-by
421
+ ``1/alpha`` branch and ``1 - alpha`` in the expanding-by
422
+ ``1/(1 - alpha)`` branch, so by the ergodic theorem its Lyapunov
423
+ exponent equals the Shannon entropy of that two-symbol Bernoulli
424
+ process -- no numerical estimation needed. This is exactly what makes
425
+ the baker's map useful as a ground truth for validating
426
+ :func:`physicskit.chaos.utils.metrics.lyapunov_exponent_from_divergence` and
427
+ similar numerical estimators.
428
+
429
+ Returns
430
+ -------
431
+ lambda_expanding, lambda_contracting : float
432
+ The two Lyapunov exponents, ``+h`` and ``-h``, where
433
+ ``h = -alpha*ln(alpha) - (1-alpha)*ln(1-alpha)`` (``= ln(2)`` for
434
+ the classic symmetric map, ``alpha=0.5``).
435
+ """
436
+ alpha = self.alpha
437
+ h = -(alpha * np.log(alpha) + (1.0 - alpha) * np.log(1.0 - alpha))
438
+ return h, -h
439
+
440
+
441
+ @njit(cache=True)
442
+ def _logistic_map_step(x: float, r: float) -> float:
443
+ """Single logistic map iteration.
444
+
445
+ Parameters
446
+ ----------
447
+ x : float
448
+ Current state, in ``[0, 1]``.
449
+ r : float
450
+ Growth rate parameter.
451
+
452
+ Returns
453
+ -------
454
+ float
455
+ Next state.
456
+ """
457
+ return r * x * (1.0 - x)
458
+
459
+
460
+ @njit(cache=True, nogil=True)
461
+ def _logistic_map_trajectory(x0: float, r: float, n_iter: int) -> NDArray[np.float64]:
462
+ """Iterate the logistic map ``n_iter`` times from ``x0``.
463
+
464
+ Parameters
465
+ ----------
466
+ x0 : float
467
+ Initial state, in ``[0, 1]``.
468
+ r : float
469
+ Growth rate parameter.
470
+ n_iter : int
471
+ Number of iterations.
472
+
473
+ Returns
474
+ -------
475
+ ndarray of float, shape (n_iter + 1,)
476
+ State at each iteration, including the initial value.
477
+ """
478
+ xs = np.empty(n_iter + 1)
479
+ xs[0] = x0
480
+ x = x0
481
+ for i in range(n_iter):
482
+ x = _logistic_map_step(x, r)
483
+ xs[i + 1] = x
484
+ return xs
485
+
486
+
487
+ class LogisticMap(DiscreteMap):
488
+ """The logistic map ``x' = r*x*(1-x)``: the simplest gateway to chaos.
489
+
490
+ Varying the single growth-rate parameter `r` takes this map through the
491
+ complete period-doubling route to chaos: a stable fixed point for
492
+ ``r < 3``, then successive period-doubling bifurcations at ``r`` values
493
+ that accumulate geometrically (ratio converging to the universal
494
+ Feigenbaum constant ``delta ~= 4.669``) onto the onset of chaos at
495
+ ``r ~= 3.5699``, beyond which the map is chaotic for most (but not all --
496
+ note the periodic windows, the largest at ``r ~= 3.8284``) values of `r`
497
+ up to ``r=4``.
498
+
499
+ Parameters
500
+ ----------
501
+ r : float, default 3.9
502
+ Growth rate parameter; interesting values range over ``[0, 4]``.
503
+
504
+ Attributes
505
+ ----------
506
+ r : float
507
+ Growth rate parameter.
508
+ """
509
+
510
+ #: State dimension, always 1. State is ``(x,)``.
511
+ dim = 1
512
+
513
+ def __init__(self, r: float = 3.9):
514
+ self.r = float(r)
515
+
516
+ def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
517
+ """Advance ``x`` by one map iteration.
518
+
519
+ Parameters
520
+ ----------
521
+ state : array_like of float, shape (1,)
522
+ Current state ``(x,)``.
523
+
524
+ Returns
525
+ -------
526
+ ndarray of float, shape (1,)
527
+ Next state ``(x,)``.
528
+ """
529
+ return np.array([_logistic_map_step(float(state[0]), self.r)])
530
+
531
+ def initial_state(self) -> NDArray[np.float64]:
532
+ """Default initial condition ``(0.5,)``.
533
+
534
+ Returns
535
+ -------
536
+ ndarray of float, shape (1,)
537
+ """
538
+ return np.array([0.5])
539
+
540
+ def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
541
+ """Iterate the map ``n_iter`` times starting from ``state0``.
542
+
543
+ Parameters
544
+ ----------
545
+ state0 : array_like of float, shape (1,), optional
546
+ Initial state ``(x,)``; defaults to :meth:`initial_state`.
547
+ n_iter : int, default 1000
548
+ Number of iterations.
549
+
550
+ Returns
551
+ -------
552
+ ndarray of float, shape (n_iter + 1, 1)
553
+ State at each iteration, including ``state0`` as row 0.
554
+ """
555
+ state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
556
+ xs = _logistic_map_trajectory(float(state0[0]), self.r, n_iter)
557
+ return xs.reshape(-1, 1)
File without changes
@@ -0,0 +1,76 @@
1
+ import numpy as np
2
+
3
+ from physicskit.chaos.systems.continuous import MagneticPendulum, magnetic_pendulum_rhs
4
+ from physicskit.chaos.visualizers.basins import basin_of_attraction
5
+
6
+
7
+ def test_basin_of_attraction_output_shapes():
8
+ system = MagneticPendulum()
9
+ xs, ys, labels = basin_of_attraction(
10
+ magnetic_pendulum_rhs,
11
+ system.params,
12
+ x_range=(-2.0, 2.0),
13
+ y_range=(-2.0, 2.0),
14
+ attractors=system.magnet_positions,
15
+ resolution=20,
16
+ dt=0.05,
17
+ n_steps=500,
18
+ )
19
+ assert xs.shape == (20,)
20
+ assert ys.shape == (20,)
21
+ assert labels.shape == (20, 20)
22
+ assert labels.dtype == np.int64
23
+
24
+
25
+ def test_basin_of_attraction_labels_are_valid_attractor_indices():
26
+ system = MagneticPendulum()
27
+ _, _, labels = basin_of_attraction(
28
+ magnetic_pendulum_rhs,
29
+ system.params,
30
+ x_range=(-2.0, 2.0),
31
+ y_range=(-2.0, 2.0),
32
+ attractors=system.magnet_positions,
33
+ resolution=20,
34
+ dt=0.05,
35
+ n_steps=500,
36
+ )
37
+ assert np.all(labels >= 0)
38
+ assert np.all(labels < system.magnet_positions.shape[0])
39
+
40
+
41
+ def test_basin_of_attraction_finds_every_attractor_with_symmetric_magnets():
42
+ """With 3 symmetrically-placed magnets and a grid centered on them, every
43
+ magnet should win somewhere on the grid."""
44
+ system = MagneticPendulum()
45
+ _, _, labels = basin_of_attraction(
46
+ magnetic_pendulum_rhs,
47
+ system.params,
48
+ x_range=(-2.0, 2.0),
49
+ y_range=(-2.0, 2.0),
50
+ attractors=system.magnet_positions,
51
+ resolution=60,
52
+ dt=0.05,
53
+ n_steps=500,
54
+ )
55
+ assert set(np.unique(labels).tolist()) == {0, 1, 2}
56
+
57
+
58
+ def test_basin_of_attraction_point_at_a_magnet_is_labeled_that_magnet():
59
+ """A grid point starting exactly on top of a magnet (with zero velocity)
60
+ should be classified to that same magnet."""
61
+ system = MagneticPendulum()
62
+ magnet_idx = 0
63
+ magnet = system.magnet_positions[magnet_idx]
64
+ # A tiny grid straddling the magnet position.
65
+ _xs, _ys, labels = basin_of_attraction(
66
+ magnetic_pendulum_rhs,
67
+ system.params,
68
+ x_range=(magnet[0] - 0.01, magnet[0] + 0.01),
69
+ y_range=(magnet[1] - 0.01, magnet[1] + 0.01),
70
+ attractors=system.magnet_positions,
71
+ resolution=3,
72
+ dt=0.05,
73
+ n_steps=500,
74
+ )
75
+ center_label = labels[1, 1]
76
+ assert center_label == magnet_idx