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,271 @@
1
+ r"""Split-operator FFT propagators for the time-dependent Schrodinger equation.
2
+
3
+ Both solvers implement the symmetric (Strang) splitting
4
+
5
+ .. math::
6
+
7
+ \psi(t+dt) = e^{-i \hat V dt / 2\hbar}\, e^{-i \hat T dt/\hbar}\,
8
+ e^{-i \hat V dt / 2\hbar}\, \psi(t),
9
+
10
+ which is unitary to machine precision regardless of :math:`dt` (probability
11
+ is exactly conserved up to FFT/round-off error), and second-order accurate
12
+ in :math:`dt`.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections.abc import Callable
18
+
19
+ import numpy as np
20
+
21
+ from .._compat import trapz
22
+
23
+ __all__ = ["SplitOperatorSolver1D", "SplitOperatorSolver2D"]
24
+
25
+
26
+ class SplitOperatorSolver1D:
27
+ r"""Propagate a 1D wavepacket under potential :math:`V(x)` or :math:`V(x,t)`.
28
+
29
+ Parameters
30
+ ----------
31
+ x : numpy.ndarray
32
+ Uniform spatial grid.
33
+ V : callable or array_like
34
+ Potential energy: a function ``V(x)`` (static) or ``V(x, t)``
35
+ (time-dependent, detected by inspecting the function's signature),
36
+ or a precomputed array matching ``x``.
37
+ hbar : float, default=1.0
38
+ Value of :math:`\hbar` to use.
39
+ m : float, default=1.0
40
+ Particle mass.
41
+ dt : float, default=1e-3
42
+ Time step.
43
+ """
44
+
45
+ def __init__(self, x: np.ndarray, V, hbar: float = 1.0, m: float = 1.0, dt: float = 1e-3):
46
+ self.x = np.asarray(x, dtype=float)
47
+ self.N = self.x.shape[0]
48
+ self.dx = self.x[1] - self.x[0]
49
+ self.hbar = hbar
50
+ self.m = m
51
+ self.dt = dt
52
+ self._V = V
53
+ self._time_dependent = callable(V) and _accepts_time(V, n_spatial_args=1)
54
+
55
+ self.k = 2 * np.pi * np.fft.fftfreq(self.N, d=self.dx)
56
+ self._kin_phase = np.exp(-1j * hbar * self.k**2 * dt / (2 * m))
57
+
58
+ if not self._time_dependent:
59
+ V0 = V(self.x) if callable(V) else np.asarray(V, dtype=float)
60
+ self._half_pot_phase = np.exp(-1j * V0 * dt / (2 * hbar))
61
+ else:
62
+ self._half_pot_phase = None
63
+
64
+ def _half_potential_phase(self, t: float) -> np.ndarray:
65
+ if self._half_pot_phase is not None:
66
+ return self._half_pot_phase
67
+ Vt = self._V(self.x, t)
68
+ return np.exp(-1j * Vt * self.dt / (2 * self.hbar))
69
+
70
+ def step(self, psi: np.ndarray, t: float = 0.0) -> np.ndarray:
71
+ """Advance the wavefunction by one time step ``dt``.
72
+
73
+ Parameters
74
+ ----------
75
+ psi : numpy.ndarray
76
+ Complex wavefunction at time ``t``, sampled on ``self.x``.
77
+ t : float, default=0.0
78
+ Current time (only used if the potential is time-dependent).
79
+
80
+ Returns
81
+ -------
82
+ numpy.ndarray
83
+ The wavefunction at time ``t + dt``.
84
+ """
85
+ psi = psi * self._half_potential_phase(t)
86
+ psi = np.fft.ifft(self._kin_phase * np.fft.fft(psi))
87
+ psi = psi * self._half_potential_phase(t + self.dt)
88
+ return psi
89
+
90
+ def propagate(self, psi0: np.ndarray, n_steps: int, t0: float = 0.0, save_every: int = 1) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
91
+ """Propagate an initial state and record its history.
92
+
93
+ Parameters
94
+ ----------
95
+ psi0 : numpy.ndarray
96
+ Initial wavefunction (renormalized internally).
97
+ n_steps : int
98
+ Total number of time steps to take.
99
+ t0 : float, default=0.0
100
+ Initial time.
101
+ save_every : int, default=1
102
+ Save a snapshot every ``save_every`` steps (plus the initial state).
103
+
104
+ Returns
105
+ -------
106
+ frames : numpy.ndarray
107
+ Stacked wavefunction snapshots, shape ``(n_saved, len(x))``.
108
+ times : numpy.ndarray
109
+ The time of each snapshot, shape ``(n_saved,)``.
110
+ """
111
+ psi = np.asarray(psi0, dtype=complex).copy()
112
+ psi /= np.sqrt(trapz(np.abs(psi) ** 2, self.x))
113
+
114
+ frames = [psi.copy()]
115
+ times = [t0]
116
+ t = t0
117
+ for step_idx in range(1, n_steps + 1):
118
+ psi = self.step(psi, t)
119
+ t = t0 + step_idx * self.dt
120
+ if step_idx % save_every == 0:
121
+ frames.append(psi.copy())
122
+ times.append(t)
123
+ return np.array(frames), np.array(times)
124
+
125
+ def norm(self, psi: np.ndarray) -> float:
126
+ r"""Total probability :math:`\int \lvert\psi(x)\rvert^2\,dx`.
127
+
128
+ Parameters
129
+ ----------
130
+ psi : numpy.ndarray
131
+ Wavefunction sampled on ``self.x``.
132
+
133
+ Returns
134
+ -------
135
+ float
136
+ The normalization integral (should stay at 1.0 under unitary
137
+ evolution).
138
+ """
139
+ return float(trapz(np.abs(psi) ** 2, self.x))
140
+
141
+
142
+ class SplitOperatorSolver2D:
143
+ r"""Propagate a 2D wavepacket under potential :math:`V(x,y)` or :math:`V(x,y,t)`.
144
+
145
+ Parameters
146
+ ----------
147
+ x, y : numpy.ndarray
148
+ Uniform spatial grids along each axis.
149
+ V : callable or array_like
150
+ Potential energy: a function ``V(x, y)`` (static) or ``V(x, y, t)``
151
+ (time-dependent, detected by inspecting the function's signature),
152
+ or a precomputed array matching the ``(x, y)`` meshgrid.
153
+ hbar : float, default=1.0
154
+ Value of :math:`\hbar` to use.
155
+ m : float, default=1.0
156
+ Particle mass.
157
+ dt : float, default=1e-3
158
+ Time step.
159
+ """
160
+
161
+ def __init__(self, x: np.ndarray, y: np.ndarray, V, hbar: float = 1.0, m: float = 1.0, dt: float = 1e-3):
162
+ self.x = np.asarray(x, dtype=float)
163
+ self.y = np.asarray(y, dtype=float)
164
+ self.dx = self.x[1] - self.x[0]
165
+ self.dy = self.y[1] - self.y[0]
166
+ self.hbar = hbar
167
+ self.m = m
168
+ self.dt = dt
169
+ self._V = V
170
+ self._time_dependent = callable(V) and _accepts_time(V, n_spatial_args=2)
171
+
172
+ self.X, self.Y = np.meshgrid(self.x, self.y, indexing="ij")
173
+ kx = 2 * np.pi * np.fft.fftfreq(self.x.shape[0], d=self.dx)
174
+ ky = 2 * np.pi * np.fft.fftfreq(self.y.shape[0], d=self.dy)
175
+ KX, KY = np.meshgrid(kx, ky, indexing="ij")
176
+ self._kin_phase = np.exp(-1j * hbar * (KX**2 + KY**2) * dt / (2 * m))
177
+
178
+ if not self._time_dependent:
179
+ V0 = V(self.X, self.Y) if callable(V) else np.asarray(V, dtype=float)
180
+ self._half_pot_phase = np.exp(-1j * V0 * dt / (2 * hbar))
181
+ else:
182
+ self._half_pot_phase = None
183
+
184
+ def _half_potential_phase(self, t: float) -> np.ndarray:
185
+ if self._half_pot_phase is not None:
186
+ return self._half_pot_phase
187
+ Vt = self._V(self.X, self.Y, t)
188
+ return np.exp(-1j * Vt * self.dt / (2 * self.hbar))
189
+
190
+ def step(self, psi: np.ndarray, t: float = 0.0) -> np.ndarray:
191
+ """Advance the wavefunction by one time step ``dt``.
192
+
193
+ Parameters
194
+ ----------
195
+ psi : numpy.ndarray
196
+ Complex wavefunction at time ``t``, sampled on the ``(x, y)`` grid.
197
+ t : float, default=0.0
198
+ Current time (only used if the potential is time-dependent).
199
+
200
+ Returns
201
+ -------
202
+ numpy.ndarray
203
+ The wavefunction at time ``t + dt``.
204
+ """
205
+ psi = psi * self._half_potential_phase(t)
206
+ psi = np.fft.ifft2(self._kin_phase * np.fft.fft2(psi))
207
+ psi = psi * self._half_potential_phase(t + self.dt)
208
+ return psi
209
+
210
+ def propagate(self, psi0: np.ndarray, n_steps: int, t0: float = 0.0, save_every: int = 1):
211
+ """Propagate an initial state and record its history.
212
+
213
+ Parameters
214
+ ----------
215
+ psi0 : numpy.ndarray
216
+ Initial wavefunction (renormalized internally).
217
+ n_steps : int
218
+ Total number of time steps to take.
219
+ t0 : float, default=0.0
220
+ Initial time.
221
+ save_every : int, default=1
222
+ Save a snapshot every ``save_every`` steps (plus the initial state).
223
+
224
+ Returns
225
+ -------
226
+ frames : numpy.ndarray
227
+ Stacked wavefunction snapshots, shape ``(n_saved, *X.shape)``.
228
+ times : numpy.ndarray
229
+ The time of each snapshot, shape ``(n_saved,)``.
230
+ """
231
+ psi = np.asarray(psi0, dtype=complex).copy()
232
+ psi /= np.sqrt(self.norm(psi))
233
+
234
+ frames = [psi.copy()]
235
+ times = [t0]
236
+ t = t0
237
+ for step_idx in range(1, n_steps + 1):
238
+ psi = self.step(psi, t)
239
+ t = t0 + step_idx * self.dt
240
+ if step_idx % save_every == 0:
241
+ frames.append(psi.copy())
242
+ times.append(t)
243
+ return np.array(frames), np.array(times)
244
+
245
+ def norm(self, psi: np.ndarray) -> float:
246
+ r"""Total probability :math:`\iint \lvert\psi(x,y)\rvert^2\,dx\,dy`.
247
+
248
+ Parameters
249
+ ----------
250
+ psi : numpy.ndarray
251
+ Wavefunction sampled on the ``(x, y)`` grid.
252
+
253
+ Returns
254
+ -------
255
+ float
256
+ The normalization integral (should stay at 1.0 under unitary
257
+ evolution).
258
+ """
259
+ density = np.abs(psi) ** 2
260
+ return float(trapz(trapz(density, self.y, axis=1), self.x))
261
+
262
+
263
+ def _accepts_time(func: Callable, n_spatial_args: int) -> bool:
264
+ """True if ``func`` takes more than ``n_spatial_args`` positional
265
+ parameters, i.e. it is V(x, ..., t) rather than V(x, ...)."""
266
+ try:
267
+ import inspect
268
+
269
+ return len(inspect.signature(func).parameters) > n_spatial_args
270
+ except (TypeError, ValueError):
271
+ return False
File without changes
@@ -0,0 +1,7 @@
1
+ """Test-session setup: force Matplotlib's non-interactive Agg backend
2
+ before anything imports pyplot, so the suite (and CI, which has no
3
+ display) can exercise the plotting/animation code headlessly."""
4
+
5
+ import matplotlib
6
+
7
+ matplotlib.use("Agg")
@@ -0,0 +1,225 @@
1
+ """Every dynamical demo produces a genuine frame-by-frame animation (not a
2
+ single static snapshot), and the underlying physics is spot-checked against
3
+ known behavior (spreading, tunneling, splitting, entanglement growth, ...).
4
+ """
5
+
6
+ import numpy as np
7
+ import pytest
8
+ from matplotlib.animation import FuncAnimation, PillowWriter
9
+
10
+ from physicskit.quantum._compat import trapz
11
+ from physicskit.quantum.chapters.entanglement import IsingEntangler
12
+ from physicskit.quantum.chapters.harmonic_spin import HarmonicOscillator
13
+ from physicskit.quantum.chapters.hydrogen_am import HydrogenOrbital, orbital_superposition_density
14
+ from physicskit.quantum.chapters.potentials import DoubleWellSimulator, FiniteSquareWell
15
+ from physicskit.quantum.chapters.spin import RabiProblem, SternGerlach
16
+ from physicskit.quantum.chapters.wave_packets import GaussianDispersion, propagate_double_slit
17
+ from physicskit.quantum.visualizers.bloch_sphere import animate_bloch_sphere, state_to_bloch_trajectory
18
+ from physicskit.quantum.visualizers.entanglement import animate_entanglement_growth
19
+ from physicskit.quantum.visualizers.orbitals import animate_orbital_beating
20
+ from physicskit.quantum.visualizers.wavefunctions import animate_density, animate_density_2d
21
+
22
+
23
+ def _save_and_check(anim: FuncAnimation, tmp_path, name: str):
24
+ out = tmp_path / name
25
+ anim.save(out, writer=PillowWriter(fps=10))
26
+ assert out.exists() and out.stat().st_size > 0
27
+
28
+
29
+ # 1. Double-slit: genuine 2D time propagation through a two-gap wall.
30
+ @pytest.mark.slow
31
+ def test_double_slit_propagation_animates(tmp_path):
32
+ x = np.linspace(-15, 15, 96)
33
+ y = np.linspace(-12, 12, 96)
34
+ X, Y, frames, times = propagate_double_slit(
35
+ x,
36
+ y,
37
+ x0=-8.0,
38
+ k0=8.0,
39
+ sigma_x=0.7,
40
+ sigma_y=4.0,
41
+ wall_x=0.0,
42
+ n_steps=160,
43
+ save_every=20,
44
+ dt=1e-4,
45
+ )
46
+ assert frames.shape[0] == len(times) >= 5
47
+ norm0 = float(np.sum(np.abs(frames[0]) ** 2)) * (x[1] - x[0]) * (y[1] - y[0])
48
+ norm_end = float(np.sum(np.abs(frames[-1]) ** 2)) * (x[1] - x[0]) * (y[1] - y[0])
49
+ assert abs(norm0 - norm_end) < 1e-2 # probability conserved through the slits
50
+
51
+ anim = animate_density_2d(X, Y, frames, times=times)
52
+ _save_and_check(anim, tmp_path, "double_slit.gif")
53
+
54
+
55
+ # 2. Quantum tunnelling: a wavepacket incident on a barrier splits into
56
+ # reflected + transmitted pieces.
57
+ @pytest.mark.slow
58
+ def test_barrier_tunnelling_animates(tmp_path):
59
+ barrier = FiniteSquareWell(V0=-6.0, width=1.0)
60
+ x, frames, times = barrier.wavepacket_scattering(
61
+ x_extent=25.0,
62
+ n_points=1024,
63
+ x0=-10.0,
64
+ sigma0=1.5,
65
+ k0=None,
66
+ dt=1e-3,
67
+ n_steps=5000,
68
+ save_every=500,
69
+ )
70
+ assert frames.shape[0] == len(times) >= 5
71
+
72
+ dx = x[1] - x[0]
73
+ final_density = np.abs(frames[-1]) ** 2
74
+ transmitted = float(np.sum(final_density[x > 0.5]) * dx)
75
+ reflected = float(np.sum(final_density[x < -0.5]) * dx)
76
+ assert transmitted > 0.01 # some probability tunnels through
77
+ assert reflected > 0.01 # some probability reflects
78
+ assert abs((transmitted + reflected) - 1.0) < 0.2
79
+
80
+ anim = animate_density(x, frames, times=times)
81
+ _save_and_check(anim, tmp_path, "barrier_tunnelling.gif")
82
+
83
+
84
+ # 3. Hydrogen orbitals: a two-eigenstate superposition beats in density,
85
+ # unlike either stationary orbital alone.
86
+ def test_hydrogen_orbital_beating_animates():
87
+ orb_a = HydrogenOrbital(2, 0, 0)
88
+ orb_b = HydrogenOrbital(3, 1, 0)
89
+ period = 2 * np.pi / (orb_b.energy - orb_a.energy)
90
+ times = np.linspace(0, abs(period), 4)
91
+
92
+ fig = animate_orbital_beating(orb_a, orb_b, times, n_points=12)
93
+ assert len(fig.frames) == len(times)
94
+ assert fig.data[0].value.size > 0
95
+ assert any(m["buttons"] for m in fig.layout.updatemenus) # a Play button exists
96
+
97
+ r = np.linspace(0.1, 15, 20)
98
+ theta = np.full_like(r, np.pi / 3)
99
+ phi = np.zeros_like(r)
100
+ density_0 = orbital_superposition_density(orb_a, orb_b, r, theta, phi, 0.0)
101
+ density_half = orbital_superposition_density(orb_a, orb_b, r, theta, phi, abs(period) / 2)
102
+ assert np.max(np.abs(density_0 - density_half)) > 1e-6 # genuinely time-dependent
103
+
104
+
105
+ # 4. Harmonic-oscillator eigenstate superposition: plumbing onto animate_density.
106
+ @pytest.mark.slow
107
+ def test_harmonic_superposition_animates(tmp_path):
108
+ ho = HarmonicOscillator()
109
+ x = np.linspace(-10, 10, 400)
110
+ times = np.linspace(0, 2 * np.pi / ho.omega, 12)
111
+ frames = ho.superposition_trajectory([0, 1, 2], [1.0, 1.0j, 0.5], x, times)
112
+
113
+ for psi in frames:
114
+ assert abs(trapz(np.abs(psi) ** 2, x) - 1.0) < 1e-6
115
+
116
+ anim = animate_density(x, frames, times=times)
117
+ _save_and_check(anim, tmp_path, "harmonic_superposition.gif")
118
+
119
+
120
+ # 5. Free wave-packet dispersion: plumbing onto animate_density.
121
+ @pytest.mark.slow
122
+ def test_wave_packet_dispersion_animates(tmp_path):
123
+ gd = GaussianDispersion(x0=0.0, sigma0=1.0, k0=3.0)
124
+ x = np.linspace(-30, 30, 600)
125
+ times = np.linspace(0, 6.0, 10)
126
+ frames = gd.trajectory(x, times)
127
+
128
+ widths = [gd.width(t) for t in times]
129
+ assert widths[-1] > widths[0] # the packet spreads
130
+
131
+ anim = animate_density(x, frames, times=times)
132
+ _save_and_check(anim, tmp_path, "wave_packet_dispersion.gif")
133
+
134
+
135
+ # 6. Scattering off a finite well: resonance/reflection dynamics, sharing
136
+ # FiniteSquareWell.wavepacket_scattering with the barrier case above.
137
+ @pytest.mark.slow
138
+ def test_well_scattering_animates(tmp_path):
139
+ well = FiniteSquareWell(V0=20.0, width=2.0)
140
+ x, frames, times = well.wavepacket_scattering(
141
+ x_extent=25.0,
142
+ n_points=1024,
143
+ x0=-10.0,
144
+ sigma0=1.5,
145
+ k0=None,
146
+ dt=1e-3,
147
+ n_steps=5000,
148
+ save_every=500,
149
+ )
150
+ assert frames.shape[0] == len(times) >= 5
151
+
152
+ anim = animate_density(x, frames, times=times)
153
+ _save_and_check(anim, tmp_path, "well_scattering.gif")
154
+
155
+
156
+ # 7. Stern-Gerlach: the joint spatial density splits into two lobes.
157
+ @pytest.mark.slow
158
+ def test_stern_gerlach_splitting_animates(tmp_path):
159
+ sg = SternGerlach(mu=2.0, grad_B=1.0, sigma0=1.0, sigma_x=2.0, k0=4.0)
160
+ x = np.linspace(-15, 15, 60)
161
+ y = np.linspace(-15, 15, 80)
162
+ times = np.array([0.0, 1.0, 2.5])
163
+ stack = sg.joint_density_stack(x, y, times)
164
+ assert stack.shape == (len(times), len(x), len(y))
165
+
166
+ def y_spread(density_xy):
167
+ marginal_y = density_xy.sum(axis=0)
168
+ marginal_y /= marginal_y.sum()
169
+ mean_y = np.sum(y * marginal_y)
170
+ return np.sqrt(np.sum((y - mean_y) ** 2 * marginal_y))
171
+
172
+ assert y_spread(stack[-1]) > y_spread(stack[0]) # splits apart over time
173
+
174
+ anim = animate_density_2d(x, y, stack, times=times, phase_colored=False)
175
+ _save_and_check(anim, tmp_path, "stern_gerlach.gif")
176
+
177
+
178
+ # 8. Rabi oscillations on the Bloch sphere.
179
+ @pytest.mark.slow
180
+ def test_rabi_bloch_sphere_animates(tmp_path):
181
+ rabi = RabiProblem(omega0=1.0, omega_d=1.0, Omega=0.5) # on resonance
182
+ t_pi = np.pi / rabi.Omega # a resonant pi-pulse fully inverts the population
183
+ assert rabi.excited_state_population(np.array([t_pi]))[0] > 0.999
184
+
185
+ times = np.linspace(0, t_pi, 20)
186
+ states = rabi.state_trajectory(times)
187
+ assert np.allclose(np.sum(np.abs(states) ** 2, axis=1), 1.0, atol=1e-10)
188
+
189
+ trajectory = state_to_bloch_trajectory(states)
190
+ assert np.allclose(np.linalg.norm(trajectory, axis=1), 1.0, atol=1e-8)
191
+
192
+ anim = animate_bloch_sphere(trajectory, times=times)
193
+ _save_and_check(anim, tmp_path, "rabi_bloch.gif")
194
+
195
+
196
+ # 9. Entangling Ising coupling: concurrence grows from 0 toward 1.
197
+ @pytest.mark.slow
198
+ def test_entanglement_growth_animates(tmp_path):
199
+ ising = IsingEntangler(J=1.0)
200
+ assert abs(ising.concurrence(ising.initial_state())) < 1e-12 # starts unentangled
201
+
202
+ times = np.linspace(0, np.pi / (2 * ising.J), 30)
203
+ concurrence = ising.concurrence_trajectory(times)
204
+ assert concurrence[0] < 1e-6
205
+ assert concurrence.max() > 0.99 # reaches (near-)maximal entanglement
206
+
207
+ anim = animate_entanglement_growth(times, concurrence)
208
+ _save_and_check(anim, tmp_path, "entanglement_growth.gif")
209
+
210
+
211
+ # 10. Double-well tunneling oscillation: almost pure plumbing onto animate_density.
212
+ @pytest.mark.slow
213
+ def test_double_well_tunneling_animates(tmp_path):
214
+ dw = DoubleWellSimulator(lam=0.3, a=1.5)
215
+ result = dw.solve(n_states=2)
216
+ times = np.linspace(0, result.tunneling_period, 16)
217
+ frames = dw.tunneling_wavefunction(result, times, side="left")
218
+ assert frames.shape == (len(times), len(dw.x))
219
+
220
+ left_prob = dw.left_well_probability(result, times, side="left")
221
+ assert left_prob[0] > 0.9 # starts localized on the left
222
+ assert left_prob[np.argmin(np.abs(times - result.tunneling_period / 2))] < 0.5 # tunnels to the right
223
+
224
+ anim = animate_density(dw.x, frames, times=times)
225
+ _save_and_check(anim, tmp_path, "double_well.gif")
@@ -0,0 +1,43 @@
1
+ """Test for physicskit.quantum._compat's scipy<1.15 fallback branch.
2
+
3
+ The dev/CI environment installs scipy>=1.15 (where scipy.special
4
+ already has sph_harm_y natively), so the ``except ImportError`` branch
5
+ that builds a fallback from the older, deprecated ``sph_harm`` is dead
6
+ code under the installed version -- but it is real code protecting
7
+ users on scipy>=1.10,<1.15 (the package's declared minimum), so it's
8
+ worth exercising directly rather than pragma-excluding it.
9
+
10
+ This simulates the old-scipy environment by temporarily removing
11
+ ``scipy.special.sph_harm_y`` and installing a fake ``sph_harm`` with
12
+ the old (m, n, phi, theta) signature/convention, then reloading the
13
+ compat module so it re-runs its import-time fallback logic, and
14
+ restores everything afterward regardless of outcome.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import importlib
20
+
21
+ import scipy.special as sp
22
+
23
+ import physicskit.quantum._compat as compat
24
+
25
+
26
+ def test_sph_harm_y_fallback_matches_native_implementation_when_scipy_is_old():
27
+ real_sph_harm_y = sp.sph_harm_y
28
+
29
+ def fake_old_sph_harm(m, n, phi, theta):
30
+ # The old, deprecated scipy.special.sph_harm's argument order and
31
+ # convention: (m, n, azimuthal, polar).
32
+ return real_sph_harm_y(n, m, theta, phi)
33
+
34
+ del sp.sph_harm_y
35
+ sp.sph_harm = fake_old_sph_harm
36
+ try:
37
+ importlib.reload(compat)
38
+ n, m, theta, phi = 2, 1, 0.3, 0.7
39
+ assert compat.sph_harm_y(n, m, theta, phi) == real_sph_harm_y(n, m, theta, phi)
40
+ finally:
41
+ del sp.sph_harm
42
+ sp.sph_harm_y = real_sph_harm_y
43
+ importlib.reload(compat)
@@ -0,0 +1,24 @@
1
+ """Error-path tests for NumerovSolver that test_physics_checks.py's
2
+ accuracy checks never exercise: rejecting a non-uniform grid and
3
+ rejecting a requested state count that isn't smaller than the number
4
+ of interior grid points."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import numpy as np
9
+ import pytest
10
+
11
+ from physicskit.quantum.core.eigensolvers import NumerovSolver, infinite_well
12
+
13
+
14
+ def test_numerov_solver_rejects_non_uniform_grid():
15
+ x = np.array([0.0, 0.1, 0.3, 0.6, 1.0]) # unevenly spaced
16
+ with pytest.raises(ValueError):
17
+ NumerovSolver(x, infinite_well())
18
+
19
+
20
+ def test_numerov_solver_rejects_n_states_not_smaller_than_interior_points():
21
+ x = np.linspace(0, 1, 6) # 4 interior points
22
+ solver = NumerovSolver(x, infinite_well())
23
+ with pytest.raises(ValueError):
24
+ solver.solve(n_states=4)