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,594 @@
1
+ r"""Feynman path integrals and their reduction to classical mechanics as :math:`\hbar\to 0`.
2
+
3
+ Dirac's 1933 paper "The Lagrangian in Quantum Mechanics" observed that the
4
+ quantum propagator ought to be built from :math:`\exp(iS/\hbar)`, one
5
+ factor per infinitesimal time step; Feynman (1948), and later Feynman &
6
+ Hibbs (1965) in their textbook *Quantum Mechanics and Path Integrals*,
7
+ turned that observation into a full reformulation of quantum mechanics:
8
+ the amplitude to go from :math:`(x_0,0)` to :math:`(x_f,T)` is a sum over
9
+ *every* continuous path :math:`x(t)` connecting the two endpoints, each
10
+ weighted by a phase built from the classical action functional
11
+ :math:`S[x(t)]=\int_0^T L(x,\dot x)\,dt` of that path,
12
+
13
+ .. math::
14
+
15
+ K(x_f,T;x_0,0) = \int \mathcal{D}[x(t)]\;
16
+ \exp\!\left(\frac{i}{\hbar}S[x(t)]\right).
17
+
18
+ This module implements that sum directly, by Monte Carlo sampling a large
19
+ ensemble of candidate paths and adding up their phasors
20
+ :math:`\exp(iS/\hbar)` one at a time, ordered from the path closest to the
21
+ true classical (stationary-action) trajectory outward. Two systems are
22
+ exactly solvable end to end here -- the free particle
23
+ (:func:`free_particle_classical_path`,
24
+ :func:`free_particle_classical_action`) and the harmonic oscillator
25
+ (:func:`harmonic_oscillator_classical_path`,
26
+ :func:`harmonic_oscillator_classical_action`) -- so every piece of the
27
+ machinery can be checked against a closed form.
28
+
29
+ The physical content is the emergence of the principle of stationary
30
+ action itself. Near the classical path, :math:`S` is stationary
31
+ (:math:`\delta S=0` to first order in the deformation), so nearby paths
32
+ all have nearly the same action and their phasors stay nearly aligned:
33
+ they interfere constructively. Far from the classical path, :math:`S`
34
+ varies rapidly from one path to a neighboring one, so
35
+ :math:`\exp(iS/\hbar)` spins rapidly through many full turns as the path
36
+ is varied, and the contributions of an ensemble of such paths cancel
37
+ almost completely by destructive interference (this is Feynman's own
38
+ "arrow and stopwatch" picture, reproduced here as the phasor spiral built
39
+ by :func:`feynman_phasor_partial_sums`). Shrinking :math:`\hbar` makes the
40
+ phase :math:`S/\hbar` more sensitive to path deformations, so the
41
+ "nearly aligned" neighborhood of the classical path shrinks -- in the
42
+ :math:`\hbar\to 0` limit only the classical path itself survives, and
43
+ quantum mechanics' sum over histories reduces exactly to the classical
44
+ principle of stationary (least) action, :math:`\delta S=0`.
45
+
46
+ Paths here are represented by their positions at :math:`N+1` uniformly
47
+ spaced times over :math:`[0,T]` (:math:`N` time slices,
48
+ :math:`\mathrm{d}t=T/N`), and the action of a discretized path is
49
+ computed with the standard midpoint-rule time slicing,
50
+
51
+ .. math::
52
+
53
+ S[x] \approx \sum_{i=0}^{N-1}\left[\frac{1}{2}m
54
+ \left(\frac{x_{i+1}-x_i}{\mathrm{d}t}\right)^2
55
+ - V\!\left(\frac{x_i+x_{i+1}}{2}\right)\right]\mathrm{d}t
56
+ \;\xrightarrow[N\to\infty]{}\; \int_0^T\!\left[\frac{1}{2}m\dot x^2
57
+ - V(x)\right]dt.
58
+
59
+ References
60
+ ----------
61
+ P. A. M. Dirac, "The Lagrangian in Quantum Mechanics," Physikalische
62
+ Zeitschrift der Sowjetunion 3, 64-72 (1933).
63
+
64
+ R. P. Feynman and A. R. Hibbs, *Quantum Mechanics and Path Integrals*,
65
+ McGraw-Hill (1965).
66
+ """
67
+
68
+ from __future__ import annotations
69
+
70
+ from collections.abc import Callable
71
+
72
+ import numpy as np
73
+
74
+ __all__ = [
75
+ "free_particle_classical_path",
76
+ "free_particle_classical_action",
77
+ "harmonic_oscillator_classical_path",
78
+ "harmonic_oscillator_classical_action",
79
+ "discretized_action",
80
+ "sample_random_paths",
81
+ "feynman_phasor_partial_sums",
82
+ "build_phasor_diagram",
83
+ ]
84
+
85
+
86
+ def _check_not_conjugate(omega: float, T: float) -> None:
87
+ if np.isclose(np.sin(omega * T), 0.0, atol=1e-9):
88
+ raise ValueError(
89
+ f"omega*T = {omega * T:.6g} is a multiple of pi: the two endpoints are "
90
+ "conjugate points of the harmonic oscillator, where the classical path "
91
+ "connecting them is non-unique (or does not exist). Choose a T not equal "
92
+ "to n*pi/omega for integer n."
93
+ )
94
+
95
+
96
+ def free_particle_classical_path(x0: float, xf: float, T: float, m: float, n_slices: int):
97
+ r"""Exact classical trajectory of a free particle between fixed endpoints.
98
+
99
+ With :math:`V=0` the Euler-Lagrange equation :math:`\ddot x=0` has the
100
+ unique solution connecting :math:`(x_0,0)` and :math:`(x_f,T)`: the
101
+ straight line :math:`x_{\rm cl}(t) = x_0 + (x_f-x_0)\,t/T`.
102
+
103
+ Parameters
104
+ ----------
105
+ x0 : float
106
+ Initial position, at :math:`t=0`.
107
+ xf : float
108
+ Final position, at :math:`t=T`.
109
+ T : float
110
+ Total elapsed time (must be positive).
111
+ m : float
112
+ Particle mass (unused for the free-particle trajectory itself,
113
+ kept for signature symmetry with :func:`discretized_action` and
114
+ the harmonic-oscillator counterpart).
115
+ n_slices : int
116
+ Number of time slices :math:`N`; the returned arrays have
117
+ :math:`N+1` points.
118
+
119
+ Returns
120
+ -------
121
+ t_array : ndarray, shape (n_slices + 1,)
122
+ Uniformly spaced times over :math:`[0,T]`.
123
+ x_array : ndarray, shape (n_slices + 1,)
124
+ Classical trajectory :math:`x_{\rm cl}(t)`.
125
+
126
+ Raises
127
+ ------
128
+ ValueError
129
+ If ``T <= 0`` or ``n_slices < 1``.
130
+
131
+ Examples
132
+ --------
133
+ >>> t, x = free_particle_classical_path(x0=0.0, xf=2.0, T=1.0, m=1.0, n_slices=4)
134
+ >>> x
135
+ array([0. , 0.5, 1. , 1.5, 2. ])
136
+ """
137
+ if T <= 0:
138
+ raise ValueError(f"T must be positive, got {T}.")
139
+ if n_slices < 1:
140
+ raise ValueError(f"n_slices must be >= 1, got {n_slices}.")
141
+ t_array = np.linspace(0.0, T, n_slices + 1)
142
+ x_array = x0 + (xf - x0) * t_array / T
143
+ return t_array, x_array
144
+
145
+
146
+ def free_particle_classical_action(x0: float, xf: float, T: float, m: float) -> float:
147
+ r"""Exact classical (Hamilton's principal function) action of a free particle.
148
+
149
+ .. math::
150
+
151
+ S_{\rm cl} = \frac{m(x_f-x_0)^2}{2T}.
152
+
153
+ Parameters
154
+ ----------
155
+ x0, xf : float
156
+ Initial and final positions.
157
+ T : float
158
+ Total elapsed time (must be positive).
159
+ m : float
160
+ Particle mass.
161
+
162
+ Returns
163
+ -------
164
+ float
165
+ The classical action :math:`S_{\rm cl}`.
166
+
167
+ Raises
168
+ ------
169
+ ValueError
170
+ If ``T <= 0``.
171
+
172
+ Examples
173
+ --------
174
+ >>> free_particle_classical_action(x0=0.0, xf=2.0, T=1.0, m=1.0)
175
+ 2.0
176
+ """
177
+ if T <= 0:
178
+ raise ValueError(f"T must be positive, got {T}.")
179
+ return m * (xf - x0) ** 2 / (2.0 * T)
180
+
181
+
182
+ def harmonic_oscillator_classical_path(x0: float, xf: float, T: float, m: float, omega: float, n_slices: int):
183
+ r"""Exact classical trajectory of a harmonic oscillator between fixed endpoints.
184
+
185
+ The Euler-Lagrange equation :math:`\ddot x = -\omega^2 x` has the
186
+ unique solution matching both endpoints
187
+
188
+ .. math::
189
+
190
+ x_{\rm cl}(t) = \frac{x_0\sin(\omega(T-t)) + x_f\sin(\omega t)}
191
+ {\sin(\omega T)},
192
+
193
+ valid as long as :math:`\sin(\omega T)\neq 0`; at a *conjugate point*
194
+ (:math:`\omega T` an integer multiple of :math:`\pi`) every path
195
+ through both endpoints has the same action to first order and the
196
+ classical path is not unique, so this raises instead of silently
197
+ dividing by zero.
198
+
199
+ Parameters
200
+ ----------
201
+ x0, xf : float
202
+ Initial and final positions.
203
+ T : float
204
+ Total elapsed time (must be positive).
205
+ m : float
206
+ Particle mass (unused for the trajectory itself, kept for
207
+ signature symmetry with :func:`harmonic_oscillator_classical_action`).
208
+ omega : float
209
+ Angular frequency of the oscillator.
210
+ n_slices : int
211
+ Number of time slices :math:`N`; the returned arrays have
212
+ :math:`N+1` points.
213
+
214
+ Returns
215
+ -------
216
+ t_array : ndarray, shape (n_slices + 1,)
217
+ Uniformly spaced times over :math:`[0,T]`.
218
+ x_array : ndarray, shape (n_slices + 1,)
219
+ Classical trajectory :math:`x_{\rm cl}(t)`.
220
+
221
+ Raises
222
+ ------
223
+ ValueError
224
+ If ``T <= 0``, ``n_slices < 1``, or ``omega*T`` is (numerically) a
225
+ multiple of :math:`\pi` (a conjugate point).
226
+
227
+ Examples
228
+ --------
229
+ >>> t, x = harmonic_oscillator_classical_path(x0=0.0, xf=1.0, T=1.0, m=1.0, omega=1.0, n_slices=4)
230
+ >>> bool(np.isclose(x[0], 0.0)) and bool(np.isclose(x[-1], 1.0))
231
+ True
232
+ """
233
+ if T <= 0:
234
+ raise ValueError(f"T must be positive, got {T}.")
235
+ if n_slices < 1:
236
+ raise ValueError(f"n_slices must be >= 1, got {n_slices}.")
237
+ _check_not_conjugate(omega, T)
238
+ t_array = np.linspace(0.0, T, n_slices + 1)
239
+ x_array = (x0 * np.sin(omega * (T - t_array)) + xf * np.sin(omega * t_array)) / np.sin(omega * T)
240
+ return t_array, x_array
241
+
242
+
243
+ def harmonic_oscillator_classical_action(x0: float, xf: float, T: float, m: float, omega: float) -> float:
244
+ r"""Exact classical (Hamilton's principal function) action of a harmonic oscillator.
245
+
246
+ .. math::
247
+
248
+ S_{\rm cl} = \frac{m\omega}{2\sin(\omega T)}
249
+ \left[(x_0^2+x_f^2)\cos(\omega T) - 2x_0 x_f\right].
250
+
251
+ Parameters
252
+ ----------
253
+ x0, xf : float
254
+ Initial and final positions.
255
+ T : float
256
+ Total elapsed time (must be positive).
257
+ m : float
258
+ Particle mass.
259
+ omega : float
260
+ Angular frequency of the oscillator.
261
+
262
+ Returns
263
+ -------
264
+ float
265
+ The classical action :math:`S_{\rm cl}`.
266
+
267
+ Raises
268
+ ------
269
+ ValueError
270
+ If ``T <= 0`` or ``omega*T`` is (numerically) a multiple of
271
+ :math:`\pi` (a conjugate point; see
272
+ :func:`harmonic_oscillator_classical_path`).
273
+
274
+ Examples
275
+ --------
276
+ >>> round(harmonic_oscillator_classical_action(x0=0.0, xf=0.0, T=1.0, m=1.0, omega=0.5), 6)
277
+ 0.0
278
+ """
279
+ if T <= 0:
280
+ raise ValueError(f"T must be positive, got {T}.")
281
+ _check_not_conjugate(omega, T)
282
+ return float((m * omega / (2.0 * np.sin(omega * T))) * ((x0**2 + xf**2) * np.cos(omega * T) - 2.0 * x0 * xf))
283
+
284
+
285
+ def discretized_action(x_array: np.ndarray, dt: float, m: float, V: Callable[[np.ndarray], np.ndarray]) -> float:
286
+ r"""Midpoint-rule discretized action of a single time-sliced path.
287
+
288
+ .. math::
289
+
290
+ S[x] = \sum_{i=0}^{N-1}\left[\frac{1}{2}m
291
+ \left(\frac{x_{i+1}-x_i}{\mathrm{d}t}\right)^2
292
+ - V\!\left(\frac{x_i+x_{i+1}}{2}\right)\right]\mathrm{d}t.
293
+
294
+ Parameters
295
+ ----------
296
+ x_array : ndarray, shape (N + 1,)
297
+ Path positions :math:`x_0,\dots,x_N` at :math:`N+1` uniformly
298
+ spaced times.
299
+ dt : float
300
+ Time-slice width.
301
+ m : float
302
+ Particle mass.
303
+ V : callable
304
+ Potential energy function ``V(x) -> array_like``, evaluated at
305
+ the midpoint of each slice; must accept a NumPy array.
306
+
307
+ Returns
308
+ -------
309
+ float
310
+ The discretized action :math:`S[x]`.
311
+
312
+ Examples
313
+ --------
314
+ >>> import numpy as np
315
+ >>> x = np.array([0.0, 0.5, 1.0, 1.5, 2.0])
316
+ >>> V = lambda x: 0.0 * x
317
+ >>> round(discretized_action(x, dt=0.25, m=1.0, V=V), 6)
318
+ 2.0
319
+ """
320
+ x_array = np.asarray(x_array, dtype=float)
321
+ x_i = x_array[:-1]
322
+ x_ip1 = x_array[1:]
323
+ kinetic = 0.5 * m * ((x_ip1 - x_i) / dt) ** 2
324
+ potential = V(0.5 * (x_i + x_ip1))
325
+ return float(np.sum((kinetic - potential) * dt))
326
+
327
+
328
+ def sample_random_paths(
329
+ x0: float,
330
+ xf: float,
331
+ T: float,
332
+ n_slices: int,
333
+ n_paths: int,
334
+ sigma: float,
335
+ n_modes: int = 8,
336
+ seed: int | None = None,
337
+ ) -> np.ndarray:
338
+ r"""Sample random paths between fixed endpoints as sine-series Brownian bridges.
339
+
340
+ Each path is the straight-line interpolation between the endpoints
341
+ plus a random wiggle built from a discrete sine series,
342
+
343
+ .. math::
344
+
345
+ x_i = x_0 + (x_f-x_0)\frac{i}{N} + \sigma\sum_{k=1}^{K}
346
+ \frac{a_k}{k}\sin\!\left(\frac{k\pi i}{N}\right),
347
+ \qquad a_k\sim\mathcal{N}(0,1)\ \text{i.i.d. per path},
348
+
349
+ where :math:`N` is ``n_slices`` and :math:`K` is ``n_modes``. Every
350
+ term of the sine series vanishes at :math:`i=0` and :math:`i=N`
351
+ identically, so every sampled path exactly satisfies the fixed
352
+ endpoint conditions :math:`x_0` and :math:`x_f` regardless of the
353
+ random coefficients drawn -- this is what makes it a valid "Brownian
354
+ bridge" construction. Paths are built around the straight line (not
355
+ around any particular classical trajectory), so the ensemble
356
+ generically explores the space of continuous paths between the
357
+ endpoints, and any special concentration of the phasor sum near the
358
+ true classical path is a genuine result rather than built in by
359
+ construction.
360
+
361
+ Parameters
362
+ ----------
363
+ x0, xf : float
364
+ Fixed initial and final positions shared by every sampled path.
365
+ T : float
366
+ Total elapsed time (must be positive).
367
+ n_slices : int
368
+ Number of time slices :math:`N`; each path has :math:`N+1` points.
369
+ n_paths : int
370
+ Number of random paths to draw.
371
+ sigma : float
372
+ Overall amplitude of the random wiggle.
373
+ n_modes : int, default=8
374
+ Number of sine modes :math:`K` used to build each path's wiggle;
375
+ higher modes are automatically suppressed by the :math:`1/k`
376
+ weighting, so this mainly controls the finest wiggle scale
377
+ resolved.
378
+ seed : int or None, optional
379
+ Seed for the random number generator, for reproducibility.
380
+
381
+ Returns
382
+ -------
383
+ ndarray, shape (n_paths, n_slices + 1)
384
+ Sampled paths, each row a full path :math:`x_0,\dots,x_N`.
385
+
386
+ Examples
387
+ --------
388
+ >>> paths = sample_random_paths(x0=0.0, xf=1.0, T=1.0, n_slices=10, n_paths=5, sigma=0.3, seed=0)
389
+ >>> paths.shape
390
+ (5, 11)
391
+ >>> bool(np.allclose(paths[:, 0], 0.0)) and bool(np.allclose(paths[:, -1], 1.0))
392
+ True
393
+ """
394
+ if T <= 0:
395
+ raise ValueError(f"T must be positive, got {T}.")
396
+ if n_slices < 1:
397
+ raise ValueError(f"n_slices must be >= 1, got {n_slices}.")
398
+ if n_paths < 1:
399
+ raise ValueError(f"n_paths must be >= 1, got {n_paths}.")
400
+
401
+ rng = np.random.default_rng(seed)
402
+ N = n_slices
403
+ i = np.arange(N + 1, dtype=float)
404
+ straight = x0 + (xf - x0) * i / N
405
+
406
+ k = np.arange(1, n_modes + 1, dtype=float)
407
+ sin_matrix = np.sin(np.outer(k, np.pi * i / N)) # shape (n_modes, N+1)
408
+
409
+ a = rng.standard_normal((n_paths, n_modes)) / k[None, :]
410
+ noise = sigma * (a @ sin_matrix) # shape (n_paths, N+1)
411
+
412
+ return straight[None, :] + noise
413
+
414
+
415
+ def feynman_phasor_partial_sums(
416
+ paths: np.ndarray,
417
+ actions: np.ndarray,
418
+ hbar: float,
419
+ classical_action: float | None = None,
420
+ ) -> np.ndarray:
421
+ r"""Feynman's phasor ("arrow and stopwatch") partial sums, ordered closest-to-classical first.
422
+
423
+ Sorts the given ensemble of paths by :math:`|S-S_{\rm cl}|` (ascending)
424
+ and accumulates the running sum of unit phasors
425
+ :math:`\exp(iS/\hbar)`, one path at a time, in that order. Because
426
+ :math:`S` is stationary at the classical path, paths near it have
427
+ nearly equal action and their phasors reinforce (the sum's magnitude
428
+ grows quickly at first); paths far from it have rapidly varying
429
+ action and their phasors point in essentially random directions, so
430
+ later additions contribute a slowly-converging random walk instead
431
+ of steady growth. This is exactly the data behind Feynman's
432
+ arrow-and-stopwatch phasor-spiral diagram.
433
+
434
+ Parameters
435
+ ----------
436
+ paths : ndarray, shape (n_paths, n_points)
437
+ Ensemble of paths (only used to determine ``n_paths``; the
438
+ ordering and summation depend only on ``actions``).
439
+ actions : ndarray, shape (n_paths,)
440
+ Discretized action of each path, e.g. from :func:`discretized_action`.
441
+ hbar : float
442
+ Reduced Planck constant (or an effective value thereof); must be
443
+ nonzero.
444
+ classical_action : float or None, optional
445
+ Reference action :math:`S_{\rm cl}` used to sort paths by
446
+ proximity. If None, the minimum action found in ``actions`` is
447
+ used instead.
448
+
449
+ Returns
450
+ -------
451
+ ndarray of complex, shape (n_paths + 1,)
452
+ Partial sums :math:`\sum_{j\le n}\exp(iS_j/\hbar)`, starting from
453
+ 0 (index 0) up to the full sum over all paths (index ``n_paths``),
454
+ accumulated in closest-to-classical-first order.
455
+
456
+ Raises
457
+ ------
458
+ ValueError
459
+ If ``hbar == 0`` or ``actions`` is empty.
460
+
461
+ Examples
462
+ --------
463
+ >>> import numpy as np
464
+ >>> actions = np.array([0.0, 0.1, -3.0])
465
+ >>> paths = np.zeros((3, 2))
466
+ >>> partial = feynman_phasor_partial_sums(paths, actions, hbar=1.0, classical_action=0.0)
467
+ >>> partial.shape
468
+ (4,)
469
+ >>> bool(np.isclose(partial[0], 0.0))
470
+ True
471
+ >>> bool(np.isclose(partial[-1], np.sum(np.exp(1j * actions))))
472
+ True
473
+ """
474
+ actions = np.asarray(actions, dtype=float)
475
+ if actions.size == 0:
476
+ raise ValueError("actions must be non-empty.")
477
+ if hbar == 0:
478
+ raise ValueError("hbar must be nonzero.")
479
+
480
+ reference = float(np.min(actions)) if classical_action is None else float(classical_action)
481
+ order = np.argsort(np.abs(actions - reference))
482
+ terms = np.exp(1j * actions[order] / hbar)
483
+ cumulative = np.cumsum(terms)
484
+ return np.concatenate([[0.0 + 0.0j], cumulative])
485
+
486
+
487
+ def build_phasor_diagram(
488
+ x0: float,
489
+ xf: float,
490
+ T: float,
491
+ m: float,
492
+ hbar: float,
493
+ potential: str = "free",
494
+ omega: float | None = None,
495
+ n_slices: int = 60,
496
+ n_paths: int = 400,
497
+ sigma: float = 0.5,
498
+ seed: int | None = None,
499
+ ):
500
+ r"""Build a complete Feynman phasor-spiral dataset for the free particle or harmonic oscillator.
501
+
502
+ Ties together the classical trajectory/action, a random ensemble of
503
+ candidate paths (:func:`sample_random_paths`), their discretized
504
+ actions (:func:`discretized_action`), and the resulting phasor
505
+ partial sums (:func:`feynman_phasor_partial_sums`) -- everything
506
+ :func:`~physicskit.semiclassical.visualizers.path_integral.animate_feynman_phasor_spiral`
507
+ needs to draw. The exact classical path is explicitly included as one
508
+ member of the returned ensemble, since it is the single most
509
+ important path in the whole construction.
510
+
511
+ Parameters
512
+ ----------
513
+ x0, xf : float
514
+ Fixed initial and final positions.
515
+ T : float
516
+ Total elapsed time (must be positive).
517
+ m : float
518
+ Particle mass.
519
+ hbar : float
520
+ Reduced Planck constant (or an effective value thereof); must be
521
+ nonzero. Smaller ``hbar`` concentrates the phasor sum more
522
+ tightly around the classical path.
523
+ potential : {"free", "harmonic"}, default="free"
524
+ Which exactly-solvable system to use.
525
+ omega : float or None, optional
526
+ Angular frequency; required if ``potential="harmonic"``.
527
+ n_slices : int, default=60
528
+ Number of time slices per path.
529
+ n_paths : int, default=400
530
+ Number of random paths to sample (in addition to the classical
531
+ path itself).
532
+ sigma : float, default=0.5
533
+ Amplitude of the random path wiggles, passed to
534
+ :func:`sample_random_paths`.
535
+ seed : int or None, optional
536
+ Seed for reproducible path sampling.
537
+
538
+ Returns
539
+ -------
540
+ paths : ndarray, shape (n_paths + 1, n_slices + 1)
541
+ The random ensemble with the classical path appended as the last row.
542
+ actions : ndarray, shape (n_paths + 1,)
543
+ Discretized action of every path in ``paths`` (including the
544
+ classical path's discretized action, which need not exactly
545
+ equal ``classical_action`` at finite ``n_slices``).
546
+ classical_path : ndarray, shape (n_slices + 1,)
547
+ The exact classical trajectory.
548
+ classical_action : float
549
+ The exact (closed-form) classical action.
550
+ partial_sums : ndarray of complex, shape (n_paths + 2,)
551
+ Phasor partial sums from :func:`feynman_phasor_partial_sums`,
552
+ ordered closest-to-classical-action first.
553
+
554
+ Raises
555
+ ------
556
+ ValueError
557
+ If ``potential`` is not one of ``"free"``/``"harmonic"``, or if
558
+ ``potential="harmonic"`` and ``omega`` is not given.
559
+
560
+ Examples
561
+ --------
562
+ >>> paths, actions, x_cl, S_cl, partial = build_phasor_diagram(
563
+ ... x0=0.0, xf=1.0, T=1.0, m=1.0, hbar=0.05, potential="free",
564
+ ... n_slices=20, n_paths=50, sigma=0.3, seed=0)
565
+ >>> paths.shape[0] == actions.shape[0] == 51
566
+ True
567
+ >>> partial.shape
568
+ (52,)
569
+ """
570
+ if T <= 0:
571
+ raise ValueError(f"T must be positive, got {T}.")
572
+
573
+ if potential == "free":
574
+ _, classical_path = free_particle_classical_path(x0, xf, T, m, n_slices)
575
+ classical_action = free_particle_classical_action(x0, xf, T, m)
576
+ V = lambda x: np.zeros_like(x)
577
+ elif potential == "harmonic":
578
+ if omega is None:
579
+ raise ValueError('omega must be given when potential="harmonic".')
580
+ _, classical_path = harmonic_oscillator_classical_path(x0, xf, T, m, omega, n_slices)
581
+ classical_action = harmonic_oscillator_classical_action(x0, xf, T, m, omega)
582
+ V = lambda x: 0.5 * m * omega**2 * x**2
583
+ else:
584
+ raise ValueError(f'potential must be "free" or "harmonic", got {potential!r}.')
585
+
586
+ random_paths = sample_random_paths(x0, xf, T, n_slices, n_paths, sigma, seed=seed)
587
+ paths = np.vstack([random_paths, classical_path[None, :]])
588
+
589
+ dt = T / n_slices
590
+ actions = np.array([discretized_action(p, dt, m, V) for p in paths])
591
+
592
+ partial_sums = feynman_phasor_partial_sums(paths, actions, hbar, classical_action=classical_action)
593
+
594
+ return paths, actions, classical_path, classical_action, partial_sums