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,199 @@
1
+ """Adaptive-step-size integration: embedded Dormand-Prince RK5(4).
2
+
3
+ This is deliberately offered only for the non-symplectic RK4-style path
4
+ (``rhs(state, t, params) -> ndarray``), not for the symplectic
5
+ :func:`~physicskit.integrators.fixed_step.leapfrog_step` /
6
+ :func:`~physicskit.integrators.fixed_step.yoshida4_step` family. Those
7
+ integrators conserve energy over long integrations *because* they take a
8
+ fixed step; naively varying ``dt`` from an embedded error estimate breaks
9
+ that guarantee; a genuinely adaptive symplectic scheme needs a reversible
10
+ step-size controller (see Hairer, Lubich & Wanner, *Geometric Numerical
11
+ Integration*), which is a different algorithm, not a drop-in extension of
12
+ this one. Use :func:`dopri5_integrate` for non-conservative, driven, or
13
+ stiff-ish systems where accuracy control matters more than exact energy
14
+ conservation; use the fixed-step symplectic integrators for long-time
15
+ Hamiltonian evolution.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import numpy as np
21
+ from numba import njit
22
+ from numpy.typing import NDArray
23
+
24
+ from physicskit.integrators.fixed_step import RHSFunc
25
+
26
+ __all__ = ["dopri5_step", "dopri5_integrate"]
27
+
28
+ # Dormand & Prince (1980) 5(4) coefficients (the classic "ode45" tableau).
29
+ _C2, _C3, _C4, _C5 = 1.0 / 5.0, 3.0 / 10.0, 4.0 / 5.0, 8.0 / 9.0
30
+
31
+ _A21 = 1.0 / 5.0
32
+ _A31, _A32 = 3.0 / 40.0, 9.0 / 40.0
33
+ _A41, _A42, _A43 = 44.0 / 45.0, -56.0 / 15.0, 32.0 / 9.0
34
+ _A51, _A52, _A53, _A54 = 19372.0 / 6561.0, -25360.0 / 2187.0, 64448.0 / 6561.0, -212.0 / 729.0
35
+ _A61, _A62, _A63, _A64, _A65 = 9017.0 / 3168.0, -355.0 / 33.0, 46732.0 / 5247.0, 49.0 / 176.0, -5103.0 / 18656.0
36
+
37
+ # 5th-order solution weights (= 7th-stage row, i.e. this method is FSAL).
38
+ _B1, _B3, _B4, _B5, _B6 = 35.0 / 384.0, 500.0 / 1113.0, 125.0 / 192.0, -2187.0 / 6784.0, 11.0 / 84.0
39
+ # Embedded 4th-order weights, for the error estimate y5 - y4.
40
+ _B1S, _B3S, _B4S, _B5S, _B6S, _B7S = (
41
+ 5179.0 / 57600.0,
42
+ 7571.0 / 16695.0,
43
+ 393.0 / 640.0,
44
+ -92097.0 / 339200.0,
45
+ 187.0 / 2100.0,
46
+ 1.0 / 40.0,
47
+ )
48
+ _E1 = _B1 - _B1S
49
+ _E3 = _B3 - _B3S
50
+ _E4 = _B4 - _B4S
51
+ _E5 = _B5 - _B5S
52
+ _E6 = _B6 - _B6S
53
+ _E7 = -_B7S
54
+
55
+
56
+ @njit(cache=True)
57
+ def dopri5_step(
58
+ rhs: RHSFunc,
59
+ state: NDArray[np.float64],
60
+ t: float,
61
+ dt: float,
62
+ params: NDArray[np.float64],
63
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
64
+ """Single embedded Dormand-Prince RK5(4) step, with an error estimate.
65
+
66
+ Parameters
67
+ ----------
68
+ rhs : callable
69
+ Numba-jitted right-hand-side function ``rhs(state, t, params) ->
70
+ ndarray``.
71
+ state : ndarray of float, shape (dim,)
72
+ Current state vector.
73
+ t : float
74
+ Current time.
75
+ dt : float
76
+ Step size to attempt.
77
+ params : ndarray of float
78
+ Parameter vector passed through to `rhs`.
79
+
80
+ Returns
81
+ -------
82
+ state_new : ndarray of float, shape (dim,)
83
+ The 5th-order state estimate at ``t + dt``.
84
+ error : ndarray of float, shape (dim,)
85
+ Difference between the 5th- and embedded 4th-order estimates, for
86
+ step-size control (see :func:`dopri5_integrate`).
87
+ """
88
+ k1 = rhs(state, t, params)
89
+ k2 = rhs(state + dt * (_A21 * k1), t + _C2 * dt, params)
90
+ k3 = rhs(state + dt * (_A31 * k1 + _A32 * k2), t + _C3 * dt, params)
91
+ k4 = rhs(state + dt * (_A41 * k1 + _A42 * k2 + _A43 * k3), t + _C4 * dt, params)
92
+ k5 = rhs(state + dt * (_A51 * k1 + _A52 * k2 + _A53 * k3 + _A54 * k4), t + _C5 * dt, params)
93
+ k6 = rhs(state + dt * (_A61 * k1 + _A62 * k2 + _A63 * k3 + _A64 * k4 + _A65 * k5), t + dt, params)
94
+ state_new = state + dt * (_B1 * k1 + _B3 * k3 + _B4 * k4 + _B5 * k5 + _B6 * k6)
95
+ k7 = rhs(state_new, t + dt, params) # FSAL: also the first stage of the next step
96
+ error = dt * (_E1 * k1 + _E3 * k3 + _E4 * k4 + _E5 * k5 + _E6 * k6 + _E7 * k7)
97
+ return state_new, error
98
+
99
+
100
+ @njit(cache=True)
101
+ def dopri5_integrate(
102
+ rhs: RHSFunc,
103
+ state0: NDArray[np.float64],
104
+ t0: float,
105
+ t_end: float,
106
+ dt0: float,
107
+ params: NDArray[np.float64],
108
+ rtol: float = 1e-6,
109
+ atol: float = 1e-9,
110
+ dt_min: float = 1e-12,
111
+ dt_max: float = 1e6,
112
+ safety: float = 0.9,
113
+ max_steps: int = 100_000,
114
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
115
+ """Integrate forward from ``t0`` to ``t_end`` with adaptive step-size control.
116
+
117
+ Steps are accepted or rejected from the embedded RK5(4) error estimate
118
+ against an ``atol + rtol * |state|`` tolerance (the same convention as
119
+ :func:`scipy.integrate.solve_ivp`), with the step size adjusted after
120
+ every attempt. Only forward integration (``t_end > t0``, ``dt0 > 0``) is
121
+ supported.
122
+
123
+ Parameters
124
+ ----------
125
+ rhs : callable
126
+ Numba-jitted right-hand-side function ``rhs(state, t, params) ->
127
+ ndarray``.
128
+ state0 : ndarray of float, shape (dim,)
129
+ Initial state vector.
130
+ t0, t_end : float
131
+ Integration interval, with ``t_end > t0``.
132
+ dt0 : float
133
+ Initial step size to attempt.
134
+ params : ndarray of float
135
+ Parameter vector passed through to `rhs`.
136
+ rtol, atol : float
137
+ Relative and absolute error tolerances.
138
+ dt_min, dt_max : float
139
+ Step-size bounds; a step is accepted once ``dt`` shrinks to
140
+ `dt_min` regardless of its error estimate, to guarantee progress.
141
+ safety : float, default=0.9
142
+ Safety factor applied to the step-size update.
143
+ max_steps : int
144
+ Upper bound on the number of attempted steps (accepted or
145
+ rejected), to guarantee termination.
146
+
147
+ Returns
148
+ -------
149
+ times : ndarray of float, shape (n_accepted + 1,)
150
+ Times of the accepted steps, starting at `t0` and ending at
151
+ `t_end`.
152
+ states : ndarray of float, shape (n_accepted + 1, dim)
153
+ State at each accepted step.
154
+ """
155
+ dim = state0.shape[0]
156
+ cap = 1024
157
+ times = np.empty(cap)
158
+ states = np.empty((cap, dim))
159
+ times[0] = t0
160
+ states[0] = state0
161
+
162
+ t = t0
163
+ state = state0.copy()
164
+ dt = dt0
165
+ count = 0
166
+ for _ in range(max_steps):
167
+ if t >= t_end:
168
+ break
169
+ if t + dt > t_end:
170
+ dt = t_end - t
171
+
172
+ state_new, error = dopri5_step(rhs, state, t, dt, params)
173
+ scale = atol + rtol * np.maximum(np.abs(state), np.abs(state_new))
174
+ err_norm = np.sqrt(np.mean((error / scale) ** 2))
175
+
176
+ if err_norm <= 1.0 or dt <= dt_min:
177
+ t = t + dt
178
+ state = state_new
179
+ count += 1
180
+ if count >= cap:
181
+ new_cap = cap * 2
182
+ new_times = np.empty(new_cap)
183
+ new_states = np.empty((new_cap, dim))
184
+ new_times[:cap] = times
185
+ new_states[:cap] = states
186
+ times = new_times
187
+ states = new_states
188
+ cap = new_cap
189
+ times[count] = t
190
+ states[count] = state
191
+
192
+ if err_norm == 0.0:
193
+ factor = 5.0
194
+ else:
195
+ factor = safety * err_norm ** (-0.2)
196
+ factor = min(max(factor, 0.2), 5.0)
197
+ dt = min(max(dt * factor, dt_min), dt_max)
198
+
199
+ return times[: count + 1], states[: count + 1]
@@ -0,0 +1,360 @@
1
+ """Numba-accelerated fixed-step time integrators, shared across subpackages.
2
+
3
+ Right-hand-side functions passed to these integrators must be module-level
4
+ ``@njit`` functions with the signature ``rhs(state, t, params) -> ndarray``
5
+ (for :func:`rk4_step` / :func:`rk4_integrate`) or ``force(pos, t, params) ->
6
+ ndarray`` (for :func:`leapfrog_step` / :func:`leapfrog_integrate` /
7
+ :func:`yoshida4_step` / :func:`yoshida4_integrate`). Keeping ``params`` as an
8
+ explicit ``float64`` array (rather than closing over Python scalars) lets
9
+ Numba compile these integrators once and reuse them, as first-class
10
+ functions, across many differently-parameterized systems -- e.g. sweeping a
11
+ grid of parameter values without recompiling per value.
12
+
13
+ The two symplectic integrators (:func:`leapfrog_step`, 2nd order, and
14
+ :func:`yoshida4_step`, 4th order) only apply to *separable* Hamiltonian
15
+ systems of the form ``pos'' = force(pos, t)`` (the force may not depend on
16
+ velocity); RK4 is required for non-separable systems (e.g. anything with a
17
+ Coriolis-like velocity-dependent force).
18
+
19
+ Callers that need a closure-based calling convention instead (a fixed
20
+ ``force_func(q, t)`` plus a per-instance ``mass_inv``, as used by
21
+ :mod:`physicskit.classical.core.integrators` for momentum-parameterized
22
+ Hamiltonian systems) are not a good fit for the ``params``-array convention
23
+ here -- baking ``mass_inv`` into a per-step closure only pays off when the
24
+ closure is built once per system instance, which conflicts with a runtime
25
+ ``params`` array meant to be swept without recompilation. That subpackage
26
+ keeps its own small, independently-tested step implementations rather than
27
+ adapting to this one.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from collections.abc import Callable
33
+
34
+ import numpy as np
35
+ from numba import njit
36
+ from numpy.typing import NDArray
37
+
38
+ __all__ = [
39
+ "RHSFunc",
40
+ "rk4_step",
41
+ "rk4_integrate",
42
+ "leapfrog_step",
43
+ "leapfrog_integrate",
44
+ "velocity_verlet_step",
45
+ "velocity_verlet_integrate",
46
+ "yoshida4_step",
47
+ "yoshida4_integrate",
48
+ ]
49
+
50
+ #: Signature shared by every right-hand-side / force function accepted by
51
+ #: the integrators below: ``f(state, t, params) -> derivative``.
52
+ RHSFunc = Callable[[NDArray[np.float64], float, NDArray[np.float64]], NDArray[np.float64]]
53
+
54
+
55
+ @njit(cache=True)
56
+ def rk4_step(
57
+ rhs: RHSFunc,
58
+ state: NDArray[np.float64],
59
+ t: float,
60
+ dt: float,
61
+ params: NDArray[np.float64],
62
+ ) -> NDArray[np.float64]:
63
+ """Single classical 4th-order Runge-Kutta step.
64
+
65
+ Parameters
66
+ ----------
67
+ rhs : callable
68
+ Numba-jitted right-hand-side function ``rhs(state, t, params) ->
69
+ ndarray``.
70
+ state : ndarray of float, shape (dim,)
71
+ Current state vector.
72
+ t : float
73
+ Current time.
74
+ dt : float
75
+ Step size.
76
+ params : ndarray of float
77
+ Parameter vector passed through to `rhs`.
78
+
79
+ Returns
80
+ -------
81
+ ndarray of float, shape (dim,)
82
+ The state advanced by one step of size `dt`.
83
+ """
84
+ k1 = rhs(state, t, params)
85
+ k2 = rhs(state + 0.5 * dt * k1, t + 0.5 * dt, params)
86
+ k3 = rhs(state + 0.5 * dt * k2, t + 0.5 * dt, params)
87
+ k4 = rhs(state + dt * k3, t + dt, params)
88
+ return state + (dt / 6.0) * (k1 + 2.0 * k2 + 2.0 * k3 + k4)
89
+
90
+
91
+ @njit(cache=True)
92
+ def rk4_integrate(
93
+ rhs: RHSFunc,
94
+ state0: NDArray[np.float64],
95
+ t0: float,
96
+ dt: float,
97
+ n_steps: int,
98
+ params: NDArray[np.float64],
99
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
100
+ """Integrate ``n_steps`` of RK4 starting from ``state0``.
101
+
102
+ Parameters
103
+ ----------
104
+ rhs : callable
105
+ Numba-jitted right-hand-side function ``rhs(state, t, params) ->
106
+ ndarray``.
107
+ state0 : ndarray of float, shape (dim,)
108
+ Initial state vector.
109
+ t0 : float
110
+ Initial time.
111
+ dt : float
112
+ Step size.
113
+ n_steps : int
114
+ Number of integration steps.
115
+ params : ndarray of float
116
+ Parameter vector passed through to `rhs`.
117
+
118
+ Returns
119
+ -------
120
+ times : ndarray of float, shape (n_steps + 1,)
121
+ Time at each step, starting at `t0`.
122
+ states : ndarray of float, shape (n_steps + 1, dim)
123
+ State at each step, starting at `state0`.
124
+ """
125
+ dim = state0.shape[0]
126
+ states = np.empty((n_steps + 1, dim))
127
+ times = np.empty(n_steps + 1)
128
+ states[0] = state0
129
+ times[0] = t0
130
+ state = state0.copy()
131
+ t = t0
132
+ for i in range(n_steps):
133
+ state = rk4_step(rhs, state, t, dt, params)
134
+ t = t0 + (i + 1) * dt
135
+ states[i + 1] = state
136
+ times[i + 1] = t
137
+ return times, states
138
+
139
+
140
+ @njit(cache=True)
141
+ def leapfrog_step(
142
+ force: RHSFunc,
143
+ pos: NDArray[np.float64],
144
+ vel: NDArray[np.float64],
145
+ t: float,
146
+ dt: float,
147
+ params: NDArray[np.float64],
148
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
149
+ """Single velocity-Verlet (symplectic) step for ``pos'' = force(pos, t)``.
150
+
151
+ Parameters
152
+ ----------
153
+ force : callable
154
+ Numba-jitted acceleration function ``force(pos, t, params) ->
155
+ ndarray``.
156
+ pos : ndarray of float, shape (dim,)
157
+ Current position vector.
158
+ vel : ndarray of float, shape (dim,)
159
+ Current velocity vector.
160
+ t : float
161
+ Current time.
162
+ dt : float
163
+ Step size.
164
+ params : ndarray of float
165
+ Parameter vector passed through to `force`.
166
+
167
+ Returns
168
+ -------
169
+ pos_new : ndarray of float, shape (dim,)
170
+ Position advanced by one step of size `dt`.
171
+ vel_new : ndarray of float, shape (dim,)
172
+ Velocity advanced by one step of size `dt`.
173
+ """
174
+ a0 = force(pos, t, params)
175
+ pos_new = pos + vel * dt + 0.5 * a0 * dt * dt
176
+ a1 = force(pos_new, t + dt, params)
177
+ vel_new = vel + 0.5 * (a0 + a1) * dt
178
+ return pos_new, vel_new
179
+
180
+
181
+ @njit(cache=True)
182
+ def leapfrog_integrate(
183
+ force: RHSFunc,
184
+ pos0: NDArray[np.float64],
185
+ vel0: NDArray[np.float64],
186
+ t0: float,
187
+ dt: float,
188
+ n_steps: int,
189
+ params: NDArray[np.float64],
190
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
191
+ """Integrate ``n_steps`` of the symplectic leapfrog scheme.
192
+
193
+ Parameters
194
+ ----------
195
+ force : callable
196
+ Numba-jitted acceleration function ``force(pos, t, params) ->
197
+ ndarray``.
198
+ pos0 : ndarray of float, shape (dim,)
199
+ Initial position vector.
200
+ vel0 : ndarray of float, shape (dim,)
201
+ Initial velocity vector.
202
+ t0 : float
203
+ Initial time.
204
+ dt : float
205
+ Step size.
206
+ n_steps : int
207
+ Number of integration steps.
208
+ params : ndarray of float
209
+ Parameter vector passed through to `force`.
210
+
211
+ Returns
212
+ -------
213
+ times : ndarray of float, shape (n_steps + 1,)
214
+ Time at each step, starting at `t0`.
215
+ positions : ndarray of float, shape (n_steps + 1, dim)
216
+ Position at each step, starting at `pos0`.
217
+ velocities : ndarray of float, shape (n_steps + 1, dim)
218
+ Velocity at each step, starting at `vel0`.
219
+ """
220
+ dim = pos0.shape[0]
221
+ positions = np.empty((n_steps + 1, dim))
222
+ velocities = np.empty((n_steps + 1, dim))
223
+ times = np.empty(n_steps + 1)
224
+ positions[0] = pos0
225
+ velocities[0] = vel0
226
+ times[0] = t0
227
+ pos = pos0.copy()
228
+ vel = vel0.copy()
229
+ t = t0
230
+ for i in range(n_steps):
231
+ pos, vel = leapfrog_step(force, pos, vel, t, dt, params)
232
+ t = t0 + (i + 1) * dt
233
+ positions[i + 1] = pos
234
+ velocities[i + 1] = vel
235
+ times[i + 1] = t
236
+ return times, positions, velocities
237
+
238
+
239
+ #: ``leapfrog_step``/``leapfrog_integrate`` under the name used by
240
+ #: gravitational-dynamics and molecular-dynamics literature, where this same
241
+ #: kick-drift-kick scheme is usually called "velocity Verlet".
242
+ velocity_verlet_step = leapfrog_step
243
+ velocity_verlet_integrate = leapfrog_integrate
244
+
245
+
246
+ #: Yoshida (1990) 4th-order composition coefficients: the 4th-order step is
247
+ #: three 2nd-order (leapfrog) sub-steps of sizes ``w1*dt``, ``w0*dt``,
248
+ #: ``w1*dt`` (which sum to ``dt``, since ``2*w1 + w0 == 1``).
249
+ _YOSHIDA_CBRT2 = 2.0 ** (1.0 / 3.0)
250
+ _YOSHIDA_W0 = -_YOSHIDA_CBRT2 / (2.0 - _YOSHIDA_CBRT2)
251
+ _YOSHIDA_W1 = 1.0 / (2.0 - _YOSHIDA_CBRT2)
252
+
253
+
254
+ @njit(cache=True)
255
+ def yoshida4_step(
256
+ force: RHSFunc,
257
+ pos: NDArray[np.float64],
258
+ vel: NDArray[np.float64],
259
+ t: float,
260
+ dt: float,
261
+ params: NDArray[np.float64],
262
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
263
+ """Single 4th-order symplectic (Yoshida) step for ``pos'' = force(pos, t)``.
264
+
265
+ Composes three :func:`leapfrog_step` sub-steps with Yoshida's (1990)
266
+ coefficients to raise the (still symplectic) accuracy from 2nd to 4th
267
+ order, at roughly 3x the cost per step of plain leapfrog. Because it
268
+ remains symplectic, it -- like leapfrog -- conserves energy far better
269
+ than RK4 over long integrations, now with much smaller local truncation
270
+ error too.
271
+
272
+ Parameters
273
+ ----------
274
+ force : callable
275
+ Numba-jitted acceleration function ``force(pos, t, params) ->
276
+ ndarray``.
277
+ pos : ndarray of float, shape (dim,)
278
+ Current position vector.
279
+ vel : ndarray of float, shape (dim,)
280
+ Current velocity vector.
281
+ t : float
282
+ Current time.
283
+ dt : float
284
+ Step size.
285
+ params : ndarray of float
286
+ Parameter vector passed through to `force`.
287
+
288
+ Returns
289
+ -------
290
+ pos_new : ndarray of float, shape (dim,)
291
+ Position advanced by one step of size `dt`.
292
+ vel_new : ndarray of float, shape (dim,)
293
+ Velocity advanced by one step of size `dt`.
294
+ """
295
+ h1 = _YOSHIDA_W1 * dt
296
+ h0 = _YOSHIDA_W0 * dt
297
+ pos, vel = leapfrog_step(force, pos, vel, t, h1, params)
298
+ t = t + h1
299
+ pos, vel = leapfrog_step(force, pos, vel, t, h0, params)
300
+ t = t + h0
301
+ pos, vel = leapfrog_step(force, pos, vel, t, h1, params)
302
+ return pos, vel
303
+
304
+
305
+ @njit(cache=True)
306
+ def yoshida4_integrate(
307
+ force: RHSFunc,
308
+ pos0: NDArray[np.float64],
309
+ vel0: NDArray[np.float64],
310
+ t0: float,
311
+ dt: float,
312
+ n_steps: int,
313
+ params: NDArray[np.float64],
314
+ ) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.float64]]:
315
+ """Integrate ``n_steps`` of the 4th-order symplectic Yoshida scheme.
316
+
317
+ Parameters
318
+ ----------
319
+ force : callable
320
+ Numba-jitted acceleration function ``force(pos, t, params) ->
321
+ ndarray``.
322
+ pos0 : ndarray of float, shape (dim,)
323
+ Initial position vector.
324
+ vel0 : ndarray of float, shape (dim,)
325
+ Initial velocity vector.
326
+ t0 : float
327
+ Initial time.
328
+ dt : float
329
+ Step size.
330
+ n_steps : int
331
+ Number of integration steps.
332
+ params : ndarray of float
333
+ Parameter vector passed through to `force`.
334
+
335
+ Returns
336
+ -------
337
+ times : ndarray of float, shape (n_steps + 1,)
338
+ Time at each step, starting at `t0`.
339
+ positions : ndarray of float, shape (n_steps + 1, dim)
340
+ Position at each step, starting at `pos0`.
341
+ velocities : ndarray of float, shape (n_steps + 1, dim)
342
+ Velocity at each step, starting at `vel0`.
343
+ """
344
+ dim = pos0.shape[0]
345
+ positions = np.empty((n_steps + 1, dim))
346
+ velocities = np.empty((n_steps + 1, dim))
347
+ times = np.empty(n_steps + 1)
348
+ positions[0] = pos0
349
+ velocities[0] = vel0
350
+ times[0] = t0
351
+ pos = pos0.copy()
352
+ vel = vel0.copy()
353
+ t = t0
354
+ for i in range(n_steps):
355
+ pos, vel = yoshida4_step(force, pos, vel, t, dt, params)
356
+ t = t0 + (i + 1) * dt
357
+ positions[i + 1] = pos
358
+ velocities[i + 1] = vel
359
+ times[i + 1] = t
360
+ return times, positions, velocities
File without changes
@@ -0,0 +1,101 @@
1
+ """Tests for physicskit.integrators itself.
2
+
3
+ physicskit.chaos.tests.test_integrators already exercises rk4/leapfrog/
4
+ yoshida4 correctness (via physicskit.chaos.core.integrators, which now
5
+ re-exports these same functions) -- it is not repeated here. This file
6
+ covers only what's specific to this package: that it's usable on its own
7
+ (without going through chaos), the velocity_verlet aliasing, and the new
8
+ adaptive Dormand-Prince integrator.
9
+ """
10
+
11
+ import numpy as np
12
+ import pytest
13
+ from numba import njit
14
+
15
+ from physicskit.integrators import (
16
+ dopri5_integrate,
17
+ leapfrog_step,
18
+ rk4_integrate,
19
+ velocity_verlet_step,
20
+ )
21
+
22
+
23
+ @njit
24
+ def _harmonic_force(pos, t, params):
25
+ omega = params[0]
26
+ return -(omega**2) * pos
27
+
28
+
29
+ @njit
30
+ def _harmonic_rhs(state, t, params):
31
+ omega = params[0]
32
+ out = np.empty(2)
33
+ out[0] = state[1]
34
+ out[1] = -(omega**2) * state[0]
35
+ return out
36
+
37
+
38
+ def _harmonic_energy(pos, vel, omega):
39
+ return 0.5 * vel**2 + 0.5 * omega**2 * pos**2
40
+
41
+
42
+ def test_velocity_verlet_is_an_alias_for_leapfrog():
43
+ """The two names refer to the same kick-drift-kick scheme -- 'leapfrog'
44
+ in the celestial-mechanics literature, 'velocity Verlet' in molecular
45
+ dynamics -- so they must be the identical function object, not merely
46
+ numerically equivalent copies."""
47
+ assert velocity_verlet_step is leapfrog_step
48
+
49
+
50
+ @pytest.mark.slow
51
+ def test_dopri5_matches_analytic_harmonic_oscillator():
52
+ omega = 2.0
53
+ params = np.array([omega])
54
+ state0 = np.array([1.0, 0.0])
55
+ t_end = 10.0
56
+
57
+ ts, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
58
+
59
+ assert ts[0] == 0.0
60
+ assert ts[-1] == t_end
61
+ expected = np.cos(omega * ts)
62
+ np.testing.assert_allclose(ys[:, 0], expected, atol=1e-6)
63
+
64
+
65
+ def test_dopri5_energy_drift_shrinks_with_tolerance():
66
+ omega = 2.0
67
+ params = np.array([omega])
68
+ state0 = np.array([1.0, 0.0])
69
+ t_end = 20.0
70
+
71
+ drifts = []
72
+ for rtol in (1e-4, 1e-9):
73
+ _, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=rtol, atol=rtol * 1e-3)
74
+ energy = _harmonic_energy(ys[:, 0], ys[:, 1], omega)
75
+ drifts.append(np.max(np.abs(energy - energy[0]) / energy[0]))
76
+
77
+ assert drifts[1] < drifts[0]
78
+
79
+
80
+ def test_dopri5_takes_fewer_steps_for_looser_tolerance():
81
+ omega = 2.0
82
+ params = np.array([omega])
83
+ state0 = np.array([1.0, 0.0])
84
+ t_end = 20.0
85
+
86
+ ts_tight, _ = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
87
+ ts_loose, _ = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-3, atol=1e-6)
88
+
89
+ assert len(ts_loose) < len(ts_tight)
90
+
91
+
92
+ def test_dopri5_agrees_with_fixed_step_rk4_at_tight_tolerance():
93
+ omega = 2.0
94
+ params = np.array([omega])
95
+ state0 = np.array([1.0, 0.0])
96
+ t_end = 5.0
97
+
98
+ ts, ys = dopri5_integrate(_harmonic_rhs, state0, 0.0, t_end, 0.01, params, rtol=1e-10, atol=1e-12)
99
+ _, ys_rk4 = rk4_integrate(_harmonic_rhs, state0, 0.0, 1e-4, 50000, params)
100
+
101
+ np.testing.assert_allclose(ys[-1], ys_rk4[-1], atol=1e-6)