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,31 @@
1
+ """Shared color constants for physicskit.chaos's Matplotlib and Plotly visualizers.
2
+
3
+ Every visualizer still accepts its own color/colormap keyword arguments to
4
+ override these, so nothing here is load-bearing -- it exists so that "the
5
+ trajectory color" or "the boundary color" is defined once, consistently,
6
+ rather than each visualizer module picking its own ad hoc literal string.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ #: Primary data color: trajectories, orbits, scatter points, histograms.
12
+ PRIMARY: str = "steelblue"
13
+
14
+ #: Accent/highlight color: a second trajectory, a moving point, a fit line, a mean marker.
15
+ ACCENT: str = "crimson"
16
+
17
+ #: Structural elements: billiard boundaries, walls, reference geometry.
18
+ STRUCTURE: str = "black"
19
+
20
+ #: Muted/reference color: guide lines, surrogate distributions, secondary annotations.
21
+ MUTED: str = "gray"
22
+
23
+ #: Qualitative colormap name for discretely-labeled data (e.g. basins of attraction).
24
+ QUALITATIVE_CMAP: str = "tab10"
25
+
26
+ #: Sequential colormap name for scalar-valued data (e.g. a trajectory colored by time or speed).
27
+ SEQUENTIAL_CMAP: str = "viridis"
28
+
29
+ #: Diverging colormap name for signed scalar data centered at zero (e.g. a wavefunction's
30
+ #: amplitude, where sign as well as magnitude matters).
31
+ DIVERGING_CMAP: str = "RdBu_r"
@@ -0,0 +1,82 @@
1
+ """Interactive 3D trajectory viewing via PyVista (the ``physicskit.chaos[viz3d]``
2
+ optional extra).
3
+
4
+ PyVista renders in a real, camera-navigable 3D scene, unlike Matplotlib's
5
+ projected-2D-with-fake-depth ``mplot3d`` axes -- most useful for genuinely
6
+ 3D structures like the Lorenz/Rossler attractors, a double pendulum's
7
+ configuration-space trajectory, or a restricted three-body orbit.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import numpy as np
13
+ from numpy.typing import ArrayLike
14
+
15
+ from physicskit.chaos.visualizers import theme
16
+
17
+ try:
18
+ import pyvista as pv
19
+ except ImportError as exc: # pragma: no cover - exercised only without the extra installed
20
+ raise ImportError("physicskit.chaos.visualizers.viewer3d requires PyVista; install it with `pip install physicskit.chaos[viz3d]`.") from exc
21
+
22
+
23
+ def pyvista_trajectory(
24
+ states: ArrayLike,
25
+ components: tuple[int, int, int] = (0, 1, 2),
26
+ cmap: str = theme.SEQUENTIAL_CMAP,
27
+ line_width: float = 2.0,
28
+ color_by: str = "index",
29
+ off_screen: bool = False,
30
+ ) -> pv.Plotter:
31
+ """Render a 3D trajectory as an interactive PyVista scene.
32
+
33
+ Parameters
34
+ ----------
35
+ states : array_like of float, shape (n, dim), dim >= 3
36
+ Trajectory samples; three of each state's components are used as
37
+ ``(x, y, z)``.
38
+ components : tuple of int, default (0, 1, 2)
39
+ Indices into each state's columns to use as ``(x, y, z)`` -- e.g.
40
+ ``(0, 1, 2)`` selects the trajectory itself, but any three state
41
+ components (or, for a system with more than three, any subset) can
42
+ be chosen.
43
+ cmap : str, default "viridis" (:data:`physicskit.chaos.visualizers.theme.SEQUENTIAL_CMAP`)
44
+ Colormap the trajectory is colored by, per `color_by`.
45
+ line_width : float, default 2.0
46
+ Rendered line width, in screen pixels.
47
+ color_by : {"index", "speed"}, default "index"
48
+ Whether to color the trajectory by sample index (i.e. time) or by
49
+ local speed (the Euclidean distance between consecutive selected
50
+ points) -- the latter highlights where the trajectory moves fastest.
51
+ off_screen : bool, default False
52
+ Render off-screen (e.g. to save a screenshot from a script or in CI)
53
+ instead of opening an interactive window.
54
+
55
+ Returns
56
+ -------
57
+ pyvista.Plotter
58
+ Call :meth:`~pyvista.Plotter.show` to display interactively, or
59
+ :meth:`~pyvista.Plotter.screenshot` when `off_screen=True`.
60
+ """
61
+ states = np.asarray(states, dtype=np.float64)
62
+ points = states[:, components]
63
+
64
+ if color_by == "speed":
65
+ deltas = np.diff(points, axis=0)
66
+ scalars = np.concatenate([[0.0], np.linalg.norm(deltas, axis=1)])
67
+ else:
68
+ scalars = np.arange(points.shape[0], dtype=np.float64)
69
+
70
+ line = pv.lines_from_points(points)
71
+ line["scalars"] = scalars
72
+
73
+ plotter = pv.Plotter(off_screen=off_screen)
74
+ plotter.add_mesh(
75
+ line,
76
+ scalars="scalars",
77
+ cmap=cmap, # type: ignore[arg-type] # pyvista stubs: cmap accepts any registered name
78
+ line_width=line_width,
79
+ show_scalar_bar=False,
80
+ )
81
+ plotter.show_grid() # type: ignore[call-arg] # pyvista stubs mistype this bound method
82
+ return plotter
@@ -0,0 +1,55 @@
1
+ """physicskit.classical: classical_mechanics_kit.
2
+
3
+ Visual and computational demonstrations of classical mechanics -- from
4
+ Newtonian vector dynamics to Lagrangian variational principles,
5
+ Hamiltonian phase spaces, coupled lattice chains, and rigid body
6
+ rotations -- built on Numba-accelerated symplectic integrators.
7
+ """
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ from physicskit.classical.core.base_system import (
12
+ DynamicalSystem,
13
+ HamiltonianSystem,
14
+ LagrangianSystem,
15
+ ODESystem,
16
+ SimulationResult,
17
+ )
18
+ from physicskit.classical.systems.chains import FPUTChain, HarmonicChain, SineGordonChain
19
+ from physicskit.classical.systems.hamiltonian import HenonHeilesSystem, PendulumSwarm, pendulum_action_angle
20
+ from physicskit.classical.systems.lagrangian import BeadOnRotatingHoop, CoupledOscillators, DoublePendulum
21
+ from physicskit.classical.systems.newtonian import KeplerSystem, ProjectileMotion
22
+ from physicskit.classical.systems.rotations import (
23
+ EulerTop,
24
+ HeavySymmetricTop,
25
+ effective_potential_symmetric_top,
26
+ find_theta_equilibrium,
27
+ nutation_frequency,
28
+ precession_frequency,
29
+ )
30
+
31
+ __all__ = [
32
+ "__version__",
33
+ "DynamicalSystem",
34
+ "ODESystem",
35
+ "HamiltonianSystem",
36
+ "LagrangianSystem",
37
+ "SimulationResult",
38
+ "ProjectileMotion",
39
+ "KeplerSystem",
40
+ "DoublePendulum",
41
+ "BeadOnRotatingHoop",
42
+ "CoupledOscillators",
43
+ "HenonHeilesSystem",
44
+ "PendulumSwarm",
45
+ "pendulum_action_angle",
46
+ "HarmonicChain",
47
+ "FPUTChain",
48
+ "SineGordonChain",
49
+ "EulerTop",
50
+ "HeavySymmetricTop",
51
+ "effective_potential_symmetric_top",
52
+ "find_theta_equilibrium",
53
+ "nutation_frequency",
54
+ "precession_frequency",
55
+ ]
@@ -0,0 +1,17 @@
1
+ from physicskit.classical.core import integrators
2
+ from physicskit.classical.core.base_system import (
3
+ DynamicalSystem,
4
+ HamiltonianSystem,
5
+ LagrangianSystem,
6
+ ODESystem,
7
+ SimulationResult,
8
+ )
9
+
10
+ __all__ = [
11
+ "DynamicalSystem",
12
+ "ODESystem",
13
+ "HamiltonianSystem",
14
+ "LagrangianSystem",
15
+ "SimulationResult",
16
+ "integrators",
17
+ ]
@@ -0,0 +1,510 @@
1
+ """Abstract base classes for ODE, Lagrangian, and Hamiltonian systems.
2
+
3
+ These classes define the common interface that every physical system in
4
+ :mod:`physicskit.classical.systems` implements, and wire each system up to the
5
+ integrators in :mod:`physicskit.classical.core.integrators`. A :class:`SimulationResult`
6
+ is returned by every ``integrate`` call and is what the visualizers in
7
+ :mod:`physicskit.classical.visualizers` consume.
8
+
9
+ Notes
10
+ -----
11
+ The step/loop functions in :mod:`physicskit.classical.core.integrators` are themselves
12
+ ``@njit``-compiled and call the supplied ``force_func``/``deriv_func``
13
+ *from inside nopython code*. Numba can only do this when that callback
14
+ is itself a genuine ``@njit`` dispatcher (numba's "first-class function"
15
+ support) -- a plain bound Python method (e.g. ``self.derivatives``)
16
+ cannot be typed and would fail. Every concrete system therefore builds
17
+ its own standalone njit callbacks (conventionally stored as
18
+ ``self._force_njit`` and/or ``self._deriv_njit``, produced by a
19
+ module-level factory function that closes over the system's numeric
20
+ parameters) and the base classes below always integrate using *those*
21
+ attributes, never a bound method.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from abc import ABC, abstractmethod
27
+ from dataclasses import dataclass, field
28
+ from typing import Optional
29
+
30
+ import numpy as np
31
+
32
+ from physicskit.classical.core import integrators as intg
33
+
34
+ __all__ = [
35
+ "SimulationResult",
36
+ "DynamicalSystem",
37
+ "ODESystem",
38
+ "HamiltonianSystem",
39
+ "LagrangianSystem",
40
+ ]
41
+
42
+
43
+ @dataclass
44
+ class SimulationResult:
45
+ """Container for the output of a system's ``integrate`` call."""
46
+
47
+ t: np.ndarray
48
+ """ndarray, shape (n_steps + 1,): Time samples."""
49
+
50
+ y: np.ndarray
51
+ """ndarray, shape (n_steps + 1, state_dim): The raw stacked-state
52
+ trajectory, in whatever layout the producing system uses internally."""
53
+
54
+ q: Optional[np.ndarray] = None
55
+ """ndarray, shape (n_steps + 1, ndof), optional: Generalized positions,
56
+ for Hamiltonian/Lagrangian systems."""
57
+
58
+ p: Optional[np.ndarray] = None
59
+ """ndarray, shape (n_steps + 1, ndof), optional: Canonical momenta for
60
+ Hamiltonian systems, or qdot for Lagrangian systems."""
61
+
62
+ energy: Optional[np.ndarray] = None
63
+ """ndarray, shape (n_steps + 1,), optional: Total mechanical energy
64
+ H(t) (or T+V) at each sample."""
65
+
66
+ method: str = ""
67
+ """str: Name of the integrator used (e.g. ``"yoshida4"``)."""
68
+
69
+ extra: dict = field(default_factory=dict)
70
+ """dict: Free-form slot for any additional diagnostics a system
71
+ chooses to attach."""
72
+
73
+
74
+ class DynamicalSystem(ABC):
75
+ """Common base for any system with a state vector evolving in time."""
76
+
77
+ def __init__(self, state0):
78
+ self.state = np.asarray(state0, dtype=np.float64)
79
+ self.t = 0.0
80
+
81
+ @abstractmethod
82
+ def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
83
+ """Return dstate/dt at ``(t, state)``.
84
+
85
+ Parameters
86
+ ----------
87
+ t : float
88
+ Current time.
89
+ state : ndarray
90
+ Current state vector.
91
+
92
+ Returns
93
+ -------
94
+ ndarray
95
+ Time derivative of ``state``.
96
+ """
97
+
98
+ @abstractmethod
99
+ def energy(self, state: Optional[np.ndarray] = None) -> float:
100
+ """Return the total mechanical energy of ``state``.
101
+
102
+ Parameters
103
+ ----------
104
+ state : ndarray, optional
105
+ State to evaluate; defaults to ``self.state``.
106
+
107
+ Returns
108
+ -------
109
+ float
110
+ Total mechanical energy.
111
+ """
112
+
113
+ def reset(self, state0=None, t0: float = 0.0):
114
+ """Reset the system's state and clock.
115
+
116
+ Parameters
117
+ ----------
118
+ state0 : array-like, optional
119
+ New state; if omitted, the current state is kept.
120
+ t0 : float, default 0.0
121
+ New time.
122
+
123
+ Returns
124
+ -------
125
+ ndarray
126
+ The (possibly updated) current state.
127
+ """
128
+ if state0 is not None:
129
+ self.state = np.asarray(state0, dtype=np.float64)
130
+ self.t = t0
131
+ return self.state
132
+
133
+ @abstractmethod
134
+ def integrate(self, t_span, dt: float, method: str = "rk4") -> SimulationResult:
135
+ """Integrate the system forward in time.
136
+
137
+ Parameters
138
+ ----------
139
+ t_span : tuple of float
140
+ ``(t0, t1)``, start and end time.
141
+ dt : float
142
+ Fixed step size.
143
+ method : str
144
+ Integrator to use; valid values depend on the subclass.
145
+
146
+ Returns
147
+ -------
148
+ SimulationResult
149
+ The full trajectory and diagnostics.
150
+ """
151
+
152
+
153
+ class ODESystem(DynamicalSystem):
154
+ """A general (possibly non-conservative) first-order ODE system.
155
+
156
+ Concrete subclasses must set ``self._deriv_njit`` (an ``@njit``
157
+ dispatcher with signature ``(t, state) -> dstate``) in ``__init__``.
158
+ Suitable for systems that are not naturally Hamiltonian/symplectic,
159
+ e.g. central-force problems with drag, or rigid-body Euler equations
160
+ (whose implicit-midpoint flow exactly conserves the quadratic energy
161
+ and angular-momentum-squared invariants).
162
+ """
163
+
164
+ _deriv_njit = None
165
+
166
+ def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
167
+ return self._deriv_njit(t, state)
168
+
169
+ def integrate(self, t_span, dt: float, method: str = "implicit_midpoint") -> SimulationResult:
170
+ """Integrate the system forward in time.
171
+
172
+ Parameters
173
+ ----------
174
+ t_span : tuple of float
175
+ ``(t0, t1)``, start and end time.
176
+ dt : float
177
+ Fixed step size.
178
+ method : {"implicit_midpoint", "rk4"}
179
+ Integrator to use.
180
+
181
+ Returns
182
+ -------
183
+ SimulationResult
184
+ The full trajectory and energy diagnostics.
185
+ """
186
+ t0, t1 = t_span
187
+ n_steps = int(round((t1 - t0) / dt))
188
+ if method == "rk4":
189
+ ts, ys = intg.rk4_integrate(self._deriv_njit, self.state, t0, n_steps, dt)
190
+ elif method == "implicit_midpoint":
191
+ ts, ys = intg.implicit_midpoint_integrate(self._deriv_njit, self.state, t0, n_steps, dt)
192
+ else:
193
+ raise ValueError(f"Unknown method '{method}' for ODESystem")
194
+ energies = np.array([self.energy(y) for y in ys])
195
+ self.state = ys[-1]
196
+ self.t = ts[-1]
197
+ return SimulationResult(t=ts, y=ys, energy=energies, method=method)
198
+
199
+
200
+ class HamiltonianSystem(DynamicalSystem):
201
+ """Base class for canonical Hamiltonian systems H(q, p, t).
202
+
203
+ Subclasses set ``ndof``, ``separable``, and ``mass_inv``, and build
204
+ ``self._force_njit(q, t) -> array`` (required when ``separable`` is
205
+ True, giving -dV/dq for H = T(p) + V(q)) and/or
206
+ ``self._deriv_njit(t, y) -> dy`` for the full stacked state (required
207
+ for non-separable systems, and optional otherwise -- if omitted it
208
+ is built automatically from ``_force_njit``).
209
+
210
+ Parameters
211
+ ----------
212
+ q0, p0 : array-like, shape (ndof,)
213
+ Initial position and momentum.
214
+
215
+ Notes
216
+ -----
217
+ ``state`` is stored flattened as ``[q_1..q_n, p_1..p_n]``.
218
+ """
219
+
220
+ ndof: int = 0
221
+ """int: Number of degrees of freedom, inferred from ``q0``."""
222
+
223
+ separable: bool = True
224
+ """bool: Whether ``H = T(p) + V(q)``, enabling the Verlet/Yoshida4 integrators."""
225
+
226
+ mass_inv = 1.0
227
+ """float or ndarray: 1/m per coordinate; ``dq/dt = mass_inv * p``."""
228
+
229
+ _force_njit = None
230
+ _deriv_njit = None
231
+
232
+ def __init__(self, q0, p0):
233
+ q0 = np.asarray(q0, dtype=np.float64)
234
+ p0 = np.asarray(p0, dtype=np.float64)
235
+ self.ndof = q0.shape[0]
236
+ super().__init__(np.concatenate([q0, p0]))
237
+
238
+ @property
239
+ def q(self) -> np.ndarray:
240
+ """ndarray: Generalized positions (the first ``ndof`` entries of ``state``)."""
241
+ return self.state[: self.ndof]
242
+
243
+ @property
244
+ def p(self) -> np.ndarray:
245
+ """ndarray: Canonical momenta (the last ``ndof`` entries of ``state``)."""
246
+ return self.state[self.ndof :]
247
+
248
+ @staticmethod
249
+ def split(state: np.ndarray, ndof: int):
250
+ """Split a stacked ``[q, p]`` state vector into its two halves.
251
+
252
+ Parameters
253
+ ----------
254
+ state : ndarray, shape (2 * ndof,)
255
+ ndof : int
256
+
257
+ Returns
258
+ -------
259
+ q, p : ndarray, shape (ndof,)
260
+ """
261
+ return state[:ndof], state[ndof:]
262
+
263
+ @abstractmethod
264
+ def kinetic_energy(self, p: np.ndarray) -> float:
265
+ """Return T(p).
266
+
267
+ Parameters
268
+ ----------
269
+ p : ndarray
270
+ Canonical momenta.
271
+
272
+ Returns
273
+ -------
274
+ float
275
+ """
276
+ ...
277
+
278
+ @abstractmethod
279
+ def potential_energy(self, q: np.ndarray) -> float:
280
+ """Return V(q).
281
+
282
+ Parameters
283
+ ----------
284
+ q : ndarray
285
+ Generalized positions.
286
+
287
+ Returns
288
+ -------
289
+ float
290
+ """
291
+ ...
292
+
293
+ def force(self, q: np.ndarray, t: float = 0.0) -> np.ndarray:
294
+ """Return -dV/dq (only defined for separable systems).
295
+
296
+ Parameters
297
+ ----------
298
+ q : ndarray
299
+ Generalized positions.
300
+ t : float, default 0.0
301
+ Current time.
302
+
303
+ Returns
304
+ -------
305
+ ndarray
306
+
307
+ Raises
308
+ ------
309
+ NotImplementedError
310
+ If the system is non-separable (no ``_force_njit`` set).
311
+ """
312
+ if self._force_njit is None:
313
+ raise NotImplementedError("This system has no separable force() (non-separable Hamiltonian)")
314
+ return self._force_njit(q, t)
315
+
316
+ def _ensure_deriv_njit(self):
317
+ if self._deriv_njit is None:
318
+ if not self.separable:
319
+ raise NotImplementedError("Non-separable systems must provide self._deriv_njit")
320
+ self._deriv_njit = intg.make_separable_derivatives(self._force_njit, self.mass_inv, self.ndof)
321
+ return self._deriv_njit
322
+
323
+ def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
324
+ return self._ensure_deriv_njit()(t, state)
325
+
326
+ def energy(self, state: Optional[np.ndarray] = None) -> float:
327
+ state = self.state if state is None else state
328
+ q, p = self.split(state, self.ndof)
329
+ return self.kinetic_energy(p) + self.potential_energy(q)
330
+
331
+ def integrate(self, t_span, dt: float, method: str = "yoshida4") -> SimulationResult:
332
+ """Integrate the system forward in time.
333
+
334
+ Parameters
335
+ ----------
336
+ t_span : tuple of float
337
+ ``(t0, t1)``, start and end time.
338
+ dt : float
339
+ Fixed step size.
340
+ method : {"yoshida4", "verlet", "implicit_midpoint", "rk4"}
341
+ Integrator to use. ``"yoshida4"``/``"verlet"`` require
342
+ ``self.separable``.
343
+
344
+ Returns
345
+ -------
346
+ SimulationResult
347
+ The full ``(q, p)`` trajectory and energy diagnostics.
348
+ """
349
+ t0, t1 = t_span
350
+ n_steps = int(round((t1 - t0) / dt))
351
+ q0, p0 = self.q.copy(), self.p.copy()
352
+ if method in ("verlet", "yoshida4"):
353
+ if not self.separable:
354
+ raise ValueError(f"method='{method}' requires a separable Hamiltonian")
355
+ integ = intg.velocity_verlet_integrate if method == "verlet" else intg.yoshida4_integrate
356
+ ts, qs, ps = integ(self._force_njit, self.mass_inv, q0, p0, t0, n_steps, dt)
357
+ elif method in ("implicit_midpoint", "rk4"):
358
+ y0 = np.concatenate([q0, p0])
359
+ deriv = self._ensure_deriv_njit()
360
+ integ = intg.implicit_midpoint_integrate if method == "implicit_midpoint" else intg.rk4_integrate
361
+ ts, ys = integ(deriv, y0, t0, n_steps, dt)
362
+ qs, ps = ys[:, : self.ndof], ys[:, self.ndof :]
363
+ else:
364
+ raise ValueError(f"Unknown method '{method}' for HamiltonianSystem")
365
+ energies = np.array([self.kinetic_energy(p) + self.potential_energy(q) for q, p in zip(qs, ps)])
366
+ self.state = np.concatenate([qs[-1], ps[-1]])
367
+ self.t = ts[-1]
368
+ return SimulationResult(t=ts, y=np.concatenate([qs, ps], axis=1), q=qs, p=ps, energy=energies, method=method)
369
+
370
+
371
+ class LagrangianSystem(DynamicalSystem):
372
+ """Base class for systems defined via a Lagrangian L(q, qdot, t).
373
+
374
+ Subclasses typically obtain their equations of motion from
375
+ :class:`physicskit.classical.utils.symbolic.LagrangianEngine`, which derives both a
376
+ direct acceleration function (for reporting/plotting in (q, qdot)
377
+ coordinates, via ``self._accel_njit``) and a canonical Hamiltonian
378
+ form (``self._canonical_deriv_njit``, used for symplectic
379
+ integration via ``implicit_midpoint`` -- valid even when the mass
380
+ matrix M(q) is configuration-dependent and the system is therefore
381
+ non-separable, e.g. the double pendulum).
382
+
383
+ Parameters
384
+ ----------
385
+ q0, qdot0 : array-like, shape (ndof,)
386
+ Initial generalized position and velocity.
387
+
388
+ Notes
389
+ -----
390
+ ``state`` is stored flattened as ``[q_1..q_n, qdot_1..qdot_n]``.
391
+ """
392
+
393
+ ndof: int = 0
394
+ """int: Number of degrees of freedom, inferred from ``q0``."""
395
+
396
+ _accel_njit = None
397
+ _momentum_njit = None
398
+ _canonical_deriv_njit = None # (t, [q,p]) -> [dq, dp] = [dH/dp, -dH/dq]
399
+ _velocity_njit = None # (q, p) -> qdot = dH/dp, used to report qdot after integration
400
+ _deriv_njit = None # (t, [q,qdot]) -> [qdot, qddot], used only by method='rk4'
401
+
402
+ def __init__(self, q0, qdot0):
403
+ q0 = np.asarray(q0, dtype=np.float64)
404
+ qdot0 = np.asarray(qdot0, dtype=np.float64)
405
+ self.ndof = q0.shape[0]
406
+ super().__init__(np.concatenate([q0, qdot0]))
407
+
408
+ @property
409
+ def q(self) -> np.ndarray:
410
+ """ndarray: Generalized positions (the first ``ndof`` entries of ``state``)."""
411
+ return self.state[: self.ndof]
412
+
413
+ @property
414
+ def qdot(self) -> np.ndarray:
415
+ """ndarray: Generalized velocities (the last ``ndof`` entries of ``state``)."""
416
+ return self.state[self.ndof :]
417
+
418
+ @staticmethod
419
+ def split(state: np.ndarray, ndof: int):
420
+ """Split a stacked ``[q, qdot]`` state vector into its two halves.
421
+
422
+ Parameters
423
+ ----------
424
+ state : ndarray, shape (2 * ndof,)
425
+ ndof : int
426
+
427
+ Returns
428
+ -------
429
+ q, qdot : ndarray, shape (ndof,)
430
+ """
431
+ return state[:ndof], state[ndof:]
432
+
433
+ def acceleration(self, q: np.ndarray, qdot: np.ndarray, t: float) -> np.ndarray:
434
+ """Return qddot at ``(q, qdot, t)`` via the Euler-Lagrange equations.
435
+
436
+ Parameters
437
+ ----------
438
+ q, qdot : ndarray
439
+ t : float
440
+
441
+ Returns
442
+ -------
443
+ ndarray
444
+ """
445
+ return self._accel_njit(q, qdot, t)
446
+
447
+ def momentum(self, q: np.ndarray, qdot: np.ndarray) -> np.ndarray:
448
+ """Return the canonical momentum p = dL/dqdot at ``(q, qdot)``.
449
+
450
+ Parameters
451
+ ----------
452
+ q, qdot : ndarray
453
+
454
+ Returns
455
+ -------
456
+ ndarray
457
+ """
458
+ return self._momentum_njit(q, qdot)
459
+
460
+ @abstractmethod
461
+ def energy(self, state: Optional[np.ndarray] = None) -> float: ...
462
+
463
+ def derivatives(self, t: float, state: np.ndarray) -> np.ndarray:
464
+ q, qdot = self.split(state, self.ndof)
465
+ return np.concatenate([qdot, self.acceleration(q, qdot, t)])
466
+
467
+ def integrate(self, t_span, dt: float, method: str = "implicit_midpoint") -> SimulationResult:
468
+ """Integrate the system forward in time.
469
+
470
+ Parameters
471
+ ----------
472
+ t_span : tuple of float
473
+ ``(t0, t1)``, start and end time.
474
+ dt : float
475
+ Fixed step size.
476
+ method : {"implicit_midpoint", "rk4"}
477
+ Integrator to use. ``"implicit_midpoint"`` (the default) is
478
+ symplectic even for non-separable systems, via the
479
+ Legendre-transformed canonical form; ``"rk4"`` integrates
480
+ the direct ``(q, qdot)`` acceleration form and is not
481
+ symplectic.
482
+
483
+ Returns
484
+ -------
485
+ SimulationResult
486
+ The full ``(q, qdot)`` trajectory and energy diagnostics
487
+ (with ``result.p`` holding qdot, not a canonical momentum).
488
+ """
489
+ t0, t1 = t_span
490
+ n_steps = int(round((t1 - t0) / dt))
491
+ q0, qdot0 = self.q.copy(), self.qdot.copy()
492
+ if method == "implicit_midpoint":
493
+ p0 = self.momentum(q0, qdot0)
494
+ y0 = np.concatenate([q0, p0])
495
+ ts, ys = intg.implicit_midpoint_integrate(self._canonical_deriv_njit, y0, t0, n_steps, dt)
496
+ qs = ys[:, : self.ndof]
497
+ ps = ys[:, self.ndof :]
498
+ qdots = np.array([self._velocity_njit(q, p) for q, p in zip(qs, ps)])
499
+ ys_out = np.concatenate([qs, qdots], axis=1)
500
+ elif method == "rk4":
501
+ y0 = np.concatenate([q0, qdot0])
502
+ ts, ys_out = intg.rk4_integrate(self._deriv_njit, y0, t0, n_steps, dt)
503
+ qs = ys_out[:, : self.ndof]
504
+ qdots = ys_out[:, self.ndof :]
505
+ else:
506
+ raise ValueError(f"Unknown method '{method}' for LagrangianSystem")
507
+ energies = np.array([self.energy(np.concatenate([q, qd])) for q, qd in zip(qs, qdots)])
508
+ self.state = ys_out[-1]
509
+ self.t = ts[-1]
510
+ return SimulationResult(t=ts, y=ys_out, q=qs, p=qdots, energy=energies, method=method)