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,120 @@
1
+ """Tests for LOE/LUE/LSE and the Marchenko-Pastur law benchmark.
2
+
3
+ The bidiagonal beta-Laguerre construction (see
4
+ ``physicskit.rmt.utils.tridiagonal.sample_laguerre_beta_eigenvalues``) was
5
+ verified during development against dense X^T X / m constructions at
6
+ beta=1, 2 (two-sample KS test between pooled eigenvalues, matching to
7
+ within Monte Carlo noise) and against the exact Marchenko-Pastur law
8
+ directly (including support edges) at beta=1, 2, 4. Like the semicircle
9
+ law, Marchenko-Pastur is an *exact* N -> infinity limit (unlike the
10
+ Wigner/Atas surmises), so convergence-rate assertions here follow the
11
+ same "shrinks as N grows" philosophy as the semicircle tests.
12
+ """
13
+
14
+ import numpy as np
15
+ import pytest
16
+ from cache_utils import cached_sample
17
+
18
+ import physicskit.rmt as rmt
19
+
20
+ CLASSICAL_WISHART = [
21
+ ("LOE", rmt.ensembles.LOE, 1.0),
22
+ ("LUE", rmt.ensembles.LUE, 2.0),
23
+ ("LSE", rmt.ensembles.LSE, 4.0),
24
+ ]
25
+
26
+ GAMMA = 0.3 # aspect ratio n/m used across most tests
27
+
28
+
29
+ @pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_WISHART)
30
+ def test_beta_is_correct(name, cls, expected_beta):
31
+ ens = cls(n=50, m=200, seed=0)
32
+ assert ens.beta == expected_beta
33
+
34
+
35
+ @pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_WISHART)
36
+ def test_gamma_property(name, cls, expected_beta):
37
+ ens = cls(n=100, m=400, seed=0)
38
+ assert ens.gamma == pytest.approx(0.25)
39
+
40
+
41
+ @pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_WISHART)
42
+ def test_sample_shape_reproducibility_and_nonnegativity(name, cls, expected_beta):
43
+ ens_a = cls(n=200, m=int(200 / GAMMA), seed=123)
44
+ ens_b = cls(n=200, m=int(200 / GAMMA), seed=123)
45
+ spec_a = ens_a.sample(n_samples=5)
46
+ spec_b = ens_b.sample(n_samples=5)
47
+
48
+ assert spec_a.eigenvalues.shape == (5, 200)
49
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
50
+ # Wishart matrices are positive semi-definite -- eigenvalues >= 0.
51
+ assert np.all(spec_a.eigenvalues >= -1e-10)
52
+
53
+
54
+ @pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_WISHART)
55
+ def test_support_matches_marchenko_pastur_edges(name, cls, expected_beta):
56
+ n = 3000
57
+ m = int(n / GAMMA)
58
+ ens = cls(n=n, m=m, seed=2)
59
+ spectrum = cached_sample(ens, n_samples=1)
60
+ lo, hi = rmt.stats.mp_support(ens.gamma)
61
+ eigs = spectrum.rescaled.ravel()
62
+ # small finite-size tolerance around the exact edges
63
+ assert eigs.min() > lo - 0.15
64
+ assert eigs.max() < hi + 0.15
65
+ assert eigs.min() < lo + 0.2
66
+ assert eigs.max() > hi - 0.2
67
+
68
+
69
+ @pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_WISHART)
70
+ def test_marchenko_pastur_convergence_shrinks_with_n(name, cls, expected_beta):
71
+ # MP is an exact limit (like the semicircle law), so -- unlike the
72
+ # Wigner/Atas surmises -- convergence-to-zero across N is the right
73
+ # thing to check, via the same robust log-log trend as
74
+ # test_gaussian_semicircle.test_semicircle_convergence_rate_decreases_with_n.
75
+ # Looped manually (rather than via benchmark.convergence_curve) so
76
+ # each N's sample can be disk-cached individually.
77
+ from scipy.stats import linregress
78
+
79
+ benchmark = rmt.validation.MarchenkoPastur(gamma=GAMMA)
80
+ n_values = [50, 150, 450, 1350]
81
+
82
+ results = []
83
+ for n in n_values:
84
+ ens = cls(n=n, m=int(n / GAMMA), seed=17)
85
+ spectrum = cached_sample(ens, n_samples=25)
86
+ results.append(benchmark.validate(spectrum, seed=17))
87
+
88
+ ks_stats = np.array([r.ks_statistic for r in results])
89
+ slope = linregress(np.log(n_values), np.log(ks_stats)).slope
90
+ assert slope < -0.3, f"{name}: expected shrinking KS distance, slope={slope}"
91
+ assert ks_stats[-1] < 0.005
92
+
93
+
94
+ def test_continuum_beta_also_matches_marchenko_pastur():
95
+ ens = rmt.ensembles.LaguerreBetaEnsemble(n=800, m=int(800 / GAMMA), beta=1.7, seed=3)
96
+ spectrum = cached_sample(ens, n_samples=25)
97
+ benchmark = rmt.validation.MarchenkoPastur(gamma=GAMMA)
98
+ result = benchmark.validate(spectrum, seed=3)
99
+ assert result.ks_statistic < 0.01
100
+
101
+
102
+ def test_invalid_parameters_rejected():
103
+ with pytest.raises(ValueError):
104
+ rmt.ensembles.LOE(n=100, m=50, seed=0) # m < n
105
+ with pytest.raises(ValueError):
106
+ rmt.ensembles.LaguerreBetaEnsemble(n=100, m=200, beta=0, seed=0)
107
+ with pytest.raises(ValueError):
108
+ rmt.validation.MarchenkoPastur(gamma=1.5) # only (0, 1] supported
109
+ with pytest.raises(ValueError):
110
+ rmt.validation.MarchenkoPastur(gamma=0.0)
111
+
112
+
113
+ def test_mp_cdf_is_zero_below_and_one_above_support_and_monotonic():
114
+ gamma = 0.5
115
+ lo, hi = rmt.stats.mp_support(gamma)
116
+ x = np.linspace(lo - 0.5, hi + 0.5, 20)
117
+ cdf = rmt.stats.mp_cdf(x, gamma)
118
+ assert cdf[0] == 0.0
119
+ assert cdf[-1] == 1.0
120
+ assert np.all(np.diff(cdf) >= -1e-12)
File without changes
@@ -0,0 +1,128 @@
1
+ """Generate Haar-distributed random unitary matrices via the corrected
2
+ QR decomposition trick.
3
+
4
+ Reference: F. Mezzadri, "How to generate random matrices from the
5
+ classical compact groups", Notices of the AMS 54 (2007) 592
6
+ (arXiv:math-ph/0609050).
7
+
8
+ A naive QR decomposition of a complex Ginibre matrix does NOT produce a
9
+ Haar-distributed unitary matrix: the QR decomposition is not unique (Q can
10
+ be multiplied by any unitary diagonal matrix while R is adjusted to
11
+ compensate), and standard LAPACK routines resolve that ambiguity in a way
12
+ that is not rotation-invariant. Fixing the phases of R's diagonal so they
13
+ are all real and positive removes the ambiguity and makes Q exactly
14
+ Haar-distributed (Mezzadri's Theorem 1). Verified empirically: the
15
+ resulting matrix is unitary to machine precision, and eigenvalue phases
16
+ are uniform on the circle (see physicskit.rmt.ensembles.circular.CUE and
17
+ tests/test_circular.py).
18
+ """
19
+
20
+ import numpy as np
21
+
22
+
23
+ def haar_unitary(n: int, rng: np.random.Generator) -> np.ndarray:
24
+ """A single n x n Haar-distributed random unitary matrix.
25
+
26
+ Parameters
27
+ ----------
28
+ n : int
29
+ rng : numpy.random.Generator
30
+
31
+ Returns
32
+ -------
33
+ numpy.ndarray, shape (n, n), complex
34
+ """
35
+ z = (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
36
+ q, r = np.linalg.qr(z)
37
+ d = np.diagonal(r)
38
+ ph = d / np.abs(d)
39
+ return q * ph # broadcasts the phase correction over columns of q
40
+
41
+
42
+ def symplectic_form(n: int) -> np.ndarray:
43
+ """The n x n (n even) symplectic form Z = I_(n/2) kron [[0, 1], [-1, 0]],
44
+ used to build the "dual" of a unitary matrix for the Circular
45
+ Symplectic Ensemble (see physicskit.rmt.ensembles.circular.CSE). Satisfies
46
+ Z^2 = -I, so Z^{-1} = -Z.
47
+
48
+ Parameters
49
+ ----------
50
+ n : int
51
+ Dimension; must be even.
52
+
53
+ Returns
54
+ -------
55
+ numpy.ndarray, shape (n, n), complex
56
+ """
57
+ if n % 2 != 0:
58
+ raise ValueError("symplectic_form requires an even dimension")
59
+ block = np.array([[0.0, 1.0], [-1.0, 0.0]], dtype=complex)
60
+ return np.kron(np.eye(n // 2, dtype=complex), block)
61
+
62
+
63
+ def haar_orthogonal(n: int, rng: np.random.Generator) -> np.ndarray:
64
+ """A single n x n Haar-distributed random orthogonal matrix (O(n),
65
+ both determinant signs -- not restricted to SO(n)).
66
+
67
+ Same corrected-QR-decomposition trick as ``haar_unitary`` (Mezzadri
68
+ 2007 covers O(n) via the identical construction), with the
69
+ diagonal-sign correction (real case) in place of the phase
70
+ correction (complex case): naive QR is non-unique up to
71
+ multiplying Q's columns by +-1 while adjusting R to compensate, and
72
+ LAPACK's default resolution of that ambiguity is not rotation-
73
+ invariant. Verified empirically during development: the resulting
74
+ matrix is orthogonal to machine precision, its first column is
75
+ uniformly distributed on the unit sphere (mean 0, second moment
76
+ 1/n per component), and its determinant is +1 or -1 with equal
77
+ probability -- see ``tests/test_haar.py``.
78
+
79
+ Parameters
80
+ ----------
81
+ n : int
82
+ rng : numpy.random.Generator
83
+
84
+ Returns
85
+ -------
86
+ numpy.ndarray, shape (n, n), real
87
+ """
88
+ z = rng.standard_normal((n, n))
89
+ q, r = np.linalg.qr(z)
90
+ d = np.diagonal(r)
91
+ sign = np.sign(d)
92
+ sign[sign == 0.0] = 1.0
93
+ return q * sign
94
+
95
+
96
+ def haar_symplectic(n_quaternionic: int, rng: np.random.Generator) -> np.ndarray:
97
+ """A single Haar-distributed random element of the compact
98
+ symplectic group Sp(n_quaternionic), embedded as a
99
+ 2*n_quaternionic x 2*n_quaternionic unitary matrix.
100
+
101
+ Construction: U = V^R @ V, where V is Haar-unitary on
102
+ U(2*n_quaternionic) and V^R = Z @ V^T @ Z^{-1} is its symplectic
103
+ dual (Z the symplectic form) -- the same construction already used
104
+ to sample the Circular Symplectic Ensemble
105
+ (``physicskit.rmt.ensembles.circular.CSE``), exposed here directly as a
106
+ reusable raw-matrix generator rather than only as an eigenvalue
107
+ intermediate. Verified directly (not merely inherited from CSE's
108
+ validated spacing statistics): U is unitary to machine precision,
109
+ satisfies the defining self-duality condition U^R == U exactly, and
110
+ its eigenvalues come in exact double-degenerate pairs -- see
111
+ ``tests/test_haar.py``.
112
+
113
+ Parameters
114
+ ----------
115
+ n_quaternionic : int
116
+ The quaternionic dimension; the returned matrix has shape
117
+ (2*n_quaternionic, 2*n_quaternionic).
118
+ rng : numpy.random.Generator
119
+
120
+ Returns
121
+ -------
122
+ numpy.ndarray, shape (2*n_quaternionic, 2*n_quaternionic), complex
123
+ """
124
+ n = 2 * n_quaternionic
125
+ v = haar_unitary(n, rng)
126
+ z = symplectic_form(n)
127
+ v_dual = (-z) @ v.T @ z # Z^{-1} = -Z since Z^2 = -I
128
+ return v_dual @ v
@@ -0,0 +1,24 @@
1
+ """Centralized RNG handling so every ensemble and benchmark in physicskit.rmt
2
+ is exactly reproducible from a seed."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import numpy as np
7
+
8
+
9
+ def as_generator(seed: int | np.random.Generator | None) -> np.random.Generator:
10
+ """Coerce ``seed`` into a numpy Generator.
11
+
12
+ Parameters
13
+ ----------
14
+ seed : int, numpy.random.Generator, or None
15
+ An existing Generator (returned as-is), an integer seed, or
16
+ None (fresh, non-reproducible entropy).
17
+
18
+ Returns
19
+ -------
20
+ numpy.random.Generator
21
+ """
22
+ if isinstance(seed, np.random.Generator):
23
+ return seed
24
+ return np.random.default_rng(seed)
@@ -0,0 +1,167 @@
1
+ """Dumitriu & Edelman (2002) tridiagonal model for the Gaussian beta-ensemble.
2
+
3
+ Reference: I. Dumitriu and A. Edelman, "Matrix Models for Beta Ensembles",
4
+ J. Math. Phys. 43, 5830 (2002).
5
+
6
+ The eigenvalues of the tridiagonal matrix
7
+
8
+ H_beta = tridiag( off_diag, diag, off_diag )
9
+
10
+ with
11
+
12
+ diag[i] ~ N(0, 2) i.i.d., i = 1..n
13
+ off_diag[i] ~ chi(df = beta * (n - i)) independent, i = 1..n-1
14
+
15
+ have exactly the joint eigenvalue density of the Gaussian beta-ensemble
16
+
17
+ f(x_1, ..., x_n) ~ exp(-beta/4 * sum_i x_i^2) * prod_{i<j} ``|x_i - x_j|^beta``
18
+
19
+ for beta = 1, 2, 4 this recovers GOE, GUE, GSE eigenvalue statistics exactly
20
+ (not just asymptotically), and for any beta > 0 it gives the continuum
21
+ Dyson-index generalization -- without ever forming or diagonalizing a dense
22
+ N x N matrix. Diagonalizing a tridiagonal matrix is O(n^2) rather than the
23
+ O(n^3) required for a general dense Hermitian eigensolve, which is what
24
+ makes it practical to push n high enough for the asymptotic theorems
25
+ (semicircle law, Tracy-Widom edge statistics, etc.) to actually apply.
26
+
27
+ Raw eigenvalues from this construction have empirical spread that grows
28
+ like sqrt(n * beta); divide by that factor to reach the standard semicircle
29
+ normalization with support [-2, 2]. See ``natural_scale`` on
30
+ ``HermiteBetaEnsemble`` in ``physicskit.rmt.ensembles.gaussian``.
31
+ """
32
+
33
+ import numpy as np
34
+ from scipy.linalg import eigh_tridiagonal
35
+ from scipy.stats import chi
36
+
37
+
38
+ def sample_hermite_beta_eigenvalues(n: int, beta: float, rng: np.random.Generator) -> np.ndarray:
39
+ """Draw one realization of eigenvalues from the beta-Hermite ensemble.
40
+
41
+ Parameters
42
+ ----------
43
+ n : int
44
+ Matrix dimension.
45
+ beta : float
46
+ Dyson index. beta=1 -> GOE, beta=2 -> GUE, beta=4 -> GSE; any
47
+ beta > 0 is a valid continuum generalization.
48
+ rng : numpy.random.Generator
49
+
50
+ Returns
51
+ -------
52
+ numpy.ndarray, shape (n,)
53
+ Raw (unnormalized) eigenvalues, ascending order.
54
+ """
55
+ if n < 2:
56
+ raise ValueError("n must be at least 2 for the tridiagonal model")
57
+ diag = rng.normal(loc=0.0, scale=np.sqrt(2.0), size=n)
58
+ dof = beta * np.arange(n - 1, 0, -1) # (n-1)*beta, (n-2)*beta, ..., beta
59
+ off_diag = chi.rvs(dof, random_state=rng)
60
+ return eigh_tridiagonal(diag, off_diag, eigvals_only=True)
61
+
62
+
63
+ def sample_hermite_beta_eigenvalues_and_vectors(n: int, beta: float, rng: np.random.Generator) -> tuple[np.ndarray, np.ndarray]:
64
+ """Draw one realization of eigenvalues AND eigenvectors from the
65
+ beta-Hermite ensemble.
66
+
67
+ The tridiagonal model preserves not only the exact beta-Hermite
68
+ eigenVALUE density but also the ensemble's rotationally/unitarily-
69
+ invariant eigenVECTOR distribution (Dumitriu-Edelman 2002; also
70
+ Edelman-Sutton): since GOE/GUE/GSE (and their continuum-beta
71
+ generalization) are invariant under conjugation by an orthogonal/
72
+ unitary/symplectic change of basis, the tridiagonal model's
73
+ eigenvectors are a faithful stand-in for a dense-matrix
74
+ diagonalization's eigenvectors, not merely a computational
75
+ convenience for the eigenvalues alone. This makes them usable, e.g.,
76
+ as the delocalized reference case for inverse-participation-ratio
77
+ statistics (see ``physicskit.rmt.stats.localization``) -- verified
78
+ numerically here: the mean IPR of the columns returned matches the
79
+ exact Haar/Gaussian-vector result ``(beta/2+1)/(n*beta/2+1)`` (see
80
+ ``physicskit.rmt.stats.localization.ipr_theory`` and
81
+ ``tests/test_localization.py``).
82
+
83
+ Parameters
84
+ ----------
85
+ n : int
86
+ Matrix dimension.
87
+ beta : float
88
+ Dyson index. beta=1 -> GOE, beta=2 -> GUE, beta=4 -> GSE; any
89
+ beta > 0 is a valid continuum generalization.
90
+ rng : numpy.random.Generator
91
+
92
+ Returns
93
+ -------
94
+ eigenvalues : numpy.ndarray, shape (n,)
95
+ Raw (unnormalized) eigenvalues, ascending order.
96
+ eigenvectors : numpy.ndarray, shape (n, n)
97
+ Column ``i`` is the eigenvector for ``eigenvalues[i]``.
98
+ """
99
+ if n < 2:
100
+ raise ValueError("n must be at least 2 for the tridiagonal model")
101
+ diag = rng.normal(loc=0.0, scale=np.sqrt(2.0), size=n)
102
+ dof = beta * np.arange(n - 1, 0, -1)
103
+ off_diag = chi.rvs(dof, random_state=rng)
104
+ return eigh_tridiagonal(diag, off_diag, eigvals_only=False)
105
+
106
+
107
+ def sample_laguerre_beta_eigenvalues(m: int, n: int, beta: float, rng: np.random.Generator) -> np.ndarray:
108
+ """Draw one realization of eigenvalues from the beta-Laguerre
109
+ (Wishart) ensemble, corresponding to an m x n rectangular Gaussian
110
+ data matrix (m "samples", n "variables", m >= n) with i.i.d.
111
+ unit-variance entries -- real for beta=1, complex for beta=2,
112
+ quaternionic for beta=4; any beta > 0 is a valid continuum
113
+ generalization.
114
+
115
+ Construction: a classical fact (Householder-bidiagonalization of a
116
+ Gaussian matrix yields independent chi-distributed bidiagonal
117
+ entries) generalized to arbitrary beta following the same pattern as
118
+ the Dumitriu-Edelman tridiagonal Hermite model. The n x n bidiagonal
119
+ matrix
120
+
121
+ B = bidiag( diagonal: chi(beta*m), chi(beta*(m-1)), ..., chi(beta*(m-n+1));
122
+ subdiagonal: chi(beta*(n-1)), chi(beta*(n-2)), ..., chi(beta) )
123
+
124
+ gives W = B B^T / (beta * m) eigenvalues matching the standard
125
+ Marchenko-Pastur normalization (support [(1-sqrt(gamma))^2,
126
+ (1+sqrt(gamma))^2] with gamma = n/m as m, n -> infinity).
127
+
128
+ Verified numerically at beta=1, 2 against dense X^T X / m
129
+ constructions (real and complex Ginibre-type X respectively): pooled
130
+ eigenvalue samples agree via two-sample KS test to within Monte Carlo
131
+ noise. All three classical beta (1, 2, 4) converge to the exact
132
+ Marchenko-Pastur law with correctly located support edges.
133
+
134
+ Parameters
135
+ ----------
136
+ m : int
137
+ Number of samples (rows) of the underlying data matrix, m >= n.
138
+ n : int
139
+ Number of variables (columns) of the underlying data matrix.
140
+ beta : float
141
+ Dyson index. beta=1 -> real, beta=2 -> complex, beta=4 ->
142
+ quaternionic; any beta > 0 is a valid continuum generalization.
143
+ rng : numpy.random.Generator
144
+
145
+ Returns
146
+ -------
147
+ numpy.ndarray, shape (n,)
148
+ Raw eigenvalues (already in Marchenko-Pastur normalization,
149
+ i.e. natural_scale = 1 for this ensemble), ascending order.
150
+ """
151
+ if n < 2:
152
+ raise ValueError("n must be at least 2 for the bidiagonal model")
153
+ if m < n:
154
+ raise ValueError("m (samples) must be >= n (variables)")
155
+ diag_dof = beta * (m - np.arange(n))
156
+ sub_dof = beta * (n - 1 - np.arange(n - 1))
157
+ diag = chi.rvs(diag_dof, random_state=rng)
158
+ sub = chi.rvs(sub_dof, random_state=rng) if n > 1 else np.array([])
159
+ B = np.diag(diag)
160
+ if n > 1:
161
+ B += np.diag(sub, -1)
162
+ W = (B @ B.T) / (beta * m)
163
+ # Real advantage over dense X^T X: no m x n matrix (and no O(m*n^2)
164
+ # matrix product) is ever formed regardless of how large m is --
165
+ # generating B costs O(n) chi draws, independent of m. The final
166
+ # eigenvalue solve on the n x n matrix W is O(n^3) either way.
167
+ return np.linalg.eigvalsh(W)
@@ -0,0 +1,36 @@
1
+ from .base import Benchmark, ValidationResult
2
+ from .circular_law import CircularLaw
3
+ from .marchenko_pastur import MarchenkoPastur
4
+ from .ratio_distribution import RatioDistribution
5
+ from .real_ginibre import RealEigenvalueCountResult, RealGinibreEigenvalueCount
6
+ from .sine_kernel import CorrelationValidationResult, SineKernel
7
+ from .single_ring import SingleRingTheorem
8
+ from .tracy_widom import TracyWidom
9
+ from .universality import (
10
+ DEFAULT_ENTRY_DISTRIBUTIONS,
11
+ UniversalityResult,
12
+ check_universality,
13
+ )
14
+ from .wachter import Wachter
15
+ from .wigner_semicircle import WignerSemicircle
16
+ from .wigner_surmise import WignerSurmise
17
+
18
+ __all__ = [
19
+ "Benchmark",
20
+ "ValidationResult",
21
+ "WignerSemicircle",
22
+ "WignerSurmise",
23
+ "RatioDistribution",
24
+ "MarchenkoPastur",
25
+ "SineKernel",
26
+ "CorrelationValidationResult",
27
+ "CircularLaw",
28
+ "TracyWidom",
29
+ "Wachter",
30
+ "check_universality",
31
+ "UniversalityResult",
32
+ "DEFAULT_ENTRY_DISTRIBUTIONS",
33
+ "RealGinibreEigenvalueCount",
34
+ "RealEigenvalueCountResult",
35
+ "SingleRingTheorem",
36
+ ]
@@ -0,0 +1,126 @@
1
+ """Base class for paper-replication benchmarks.
2
+
3
+ A single fixed-N Kolmogorov-Smirnov test with a p > 0.05 pass criterion is
4
+ *not* a reliable way to validate convergence to an asymptotic law: KS test
5
+ power grows with N, so real, expected finite-size deviations become more
6
+ likely to trigger rejection as N grows, not less. Instead, every Benchmark
7
+ here supports ``convergence_curve``, which tracks a distance-to-theory
8
+ metric across a sequence of N values and expects it to *shrink* as N grows
9
+ -- that is the statistically meaningful way to demonstrate asymptotic
10
+ convergence.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from abc import ABC, abstractmethod
16
+ from collections.abc import Callable
17
+ from dataclasses import dataclass
18
+
19
+ import numpy as np
20
+ from scipy.stats import kstest, wasserstein_distance
21
+
22
+ from ..ensembles.base import MatrixEnsemble
23
+ from ..spectrum import Spectrum
24
+
25
+
26
+ @dataclass
27
+ class ValidationResult:
28
+ """Result of comparing one Spectrum against a benchmark's theoretical
29
+ distribution."""
30
+
31
+ ks_statistic: float
32
+ ks_pvalue: float
33
+ wasserstein_distance: float
34
+ n_eigenvalues: int
35
+
36
+ def __repr__(self) -> str:
37
+ return (
38
+ f"ValidationResult(ks_statistic={self.ks_statistic:.5f}, "
39
+ f"ks_pvalue={self.ks_pvalue:.3g}, "
40
+ f"wasserstein_distance={self.wasserstein_distance:.5f}, "
41
+ f"n_eigenvalues={self.n_eigenvalues})"
42
+ )
43
+
44
+
45
+ class Benchmark(ABC):
46
+ """Ties one theoretical distribution to a validation procedure.
47
+
48
+ Subclasses implement ``theoretical_cdf`` (for the one-sample KS test,
49
+ which needs no simulated reference sample) and ``reference_samples``
50
+ (for the Wasserstein-distance comparison, which does).
51
+ """
52
+
53
+ @abstractmethod
54
+ def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
55
+ """Exact theoretical CDF, vectorized over x."""
56
+ raise NotImplementedError # pragma: no cover -- unreachable, see MatrixEnsemble._sample_eigenvalues
57
+
58
+ @abstractmethod
59
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
60
+ """Draw ``size`` i.i.d. samples from the theoretical distribution."""
61
+ raise NotImplementedError # pragma: no cover -- unreachable, see MatrixEnsemble._sample_eigenvalues
62
+
63
+ #: Reference-sample size used for the Wasserstein comparison. Kept
64
+ #: large and fixed (rather than tied to len(data)) so the reference
65
+ #: sample's own Monte Carlo noise stays well below the eigenvalue-side
66
+ #: signal even once the ensemble has converged tightly to theory --
67
+ #: otherwise the noise floor from a same-size reference sample can
68
+ #: swamp the (small, real) improvement between two already-close N
69
+ #: values and make the distance non-monotonic by chance.
70
+ reference_size: int = 200_000
71
+
72
+ def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
73
+ """Compare a Spectrum's rescaled eigenvalues against theory.
74
+
75
+ Returns a ValidationResult with both a one-sample KS statistic
76
+ (against the exact theoretical CDF) and a Wasserstein distance
77
+ (against simulated reference samples) -- report both rather than
78
+ gating on a single p-value threshold.
79
+ """
80
+ data = spectrum.rescaled.ravel()
81
+ ks = kstest(data, self.theoretical_cdf)
82
+ rng = np.random.default_rng(seed)
83
+ reference = self.reference_samples(self.reference_size, rng)
84
+ wd = wasserstein_distance(data, reference)
85
+ return ValidationResult(
86
+ ks_statistic=float(ks.statistic),
87
+ ks_pvalue=float(ks.pvalue),
88
+ wasserstein_distance=float(wd),
89
+ n_eigenvalues=len(data),
90
+ )
91
+
92
+ def convergence_curve(
93
+ self,
94
+ ensemble_factory: Callable[..., MatrixEnsemble],
95
+ n_values: list[int],
96
+ n_samples: int = 20,
97
+ seed: int | np.random.Generator | None = None,
98
+ ) -> list[tuple[int, ValidationResult]]:
99
+ """Track distance-to-theory across increasing matrix size N.
100
+
101
+ Parameters
102
+ ----------
103
+ ensemble_factory : callable
104
+ ``ensemble_factory(n, seed=...)`` returning a fresh
105
+ ``MatrixEnsemble`` instance of size n.
106
+ n_values : sequence of int
107
+ Matrix sizes to test, in increasing order.
108
+ n_samples : int
109
+ Independent matrix draws to pool at each N.
110
+ seed : int or None
111
+
112
+ Returns
113
+ -------
114
+ list of (n, ValidationResult)
115
+ The KS statistic (and Wasserstein distance) in each result is
116
+ expected to shrink as n increases; this is the check to make,
117
+ not a single p > 0.05 threshold at one N.
118
+ """
119
+ rng = np.random.default_rng(seed)
120
+ results = []
121
+ for n in n_values:
122
+ ensemble = ensemble_factory(n, seed=int(rng.integers(1 << 31)))
123
+ spectrum = ensemble.sample(n_samples=n_samples)
124
+ result = self.validate(spectrum, seed=int(rng.integers(1 << 31)))
125
+ results.append((n, result))
126
+ return results
@@ -0,0 +1,80 @@
1
+ """Circular law benchmark for the Ginibre ensembles.
2
+
3
+ Reference: J. Ginibre, J. Math. Phys. 6 (1965) 440.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from collections.abc import Callable
9
+
10
+ import numpy as np
11
+
12
+ from ..ensembles.base import MatrixEnsemble
13
+ from ..spectrum import Spectrum
14
+ from ..stats.circular_law import circular_law_radial_cdf, circular_law_radial_pdf
15
+ from .base import Benchmark, ValidationResult
16
+
17
+
18
+ class CircularLaw(Benchmark):
19
+ """Validates a Ginibre-type Spectrum's eigenvalue *radii* against the
20
+ circular law's radial marginal (F(r) = r^2 on [0, 1]).
21
+
22
+ Overrides ``validate`` (rather than just ``theoretical_cdf`` /
23
+ ``reference_samples``) because the base class compares
24
+ ``spectrum.rescaled`` directly, but here it's the eigenvalue
25
+ *magnitudes* -- not the (complex) rescaled eigenvalues themselves --
26
+ that are compared to a 1-D theoretical distribution.
27
+ """
28
+
29
+ def theoretical_pdf(self, r: np.ndarray) -> np.ndarray:
30
+ return circular_law_radial_pdf(r)
31
+
32
+ def theoretical_cdf(self, r: np.ndarray) -> np.ndarray:
33
+ return circular_law_radial_cdf(r)
34
+
35
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
36
+ # Inverse-CDF sampling: F(r) = r^2 => r = sqrt(U), U ~ Uniform(0,1)
37
+ u = rng.uniform(0.0, 1.0, size=size)
38
+ return np.sqrt(u)
39
+
40
+ def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
41
+ radii = np.abs(spectrum.rescaled.ravel())
42
+ ks_result = self._ks_and_wasserstein(radii, seed)
43
+ return ks_result
44
+
45
+ def _ks_and_wasserstein(self, radii: np.ndarray, seed: int | np.random.Generator | None) -> ValidationResult:
46
+ from scipy.stats import kstest, wasserstein_distance
47
+
48
+ ks = kstest(radii, self.theoretical_cdf)
49
+ rng = np.random.default_rng(seed)
50
+ reference = self.reference_samples(self.reference_size, rng)
51
+ wd = wasserstein_distance(radii, reference)
52
+ return ValidationResult(
53
+ ks_statistic=float(ks.statistic),
54
+ ks_pvalue=float(ks.pvalue),
55
+ wasserstein_distance=float(wd),
56
+ n_eigenvalues=len(radii),
57
+ )
58
+
59
+ def convergence_curve(
60
+ self,
61
+ ensemble_factory: Callable[..., MatrixEnsemble],
62
+ n_values: list[int],
63
+ n_samples: int = 20,
64
+ seed: int | np.random.Generator | None = None,
65
+ ) -> list[tuple[int, ValidationResult]]:
66
+ """Same convergence-rate philosophy as the other exact-limit
67
+ benchmarks (semicircle, Marchenko-Pastur): track KS distance to
68
+ the circular law as N grows. Convergence here is known to be
69
+ slower than the Hermitian ensembles' (weaker eigenvalue rigidity
70
+ for non-Hermitian matrices), so expect a shallower log-log slope
71
+ -- verified during development to be reliably negative but
72
+ smaller in magnitude than the semicircle/MP cases."""
73
+ rng = np.random.default_rng(seed)
74
+ results = []
75
+ for n in n_values:
76
+ ensemble = ensemble_factory(n, seed=int(rng.integers(1 << 31)))
77
+ spectrum = ensemble.sample(n_samples=n_samples)
78
+ result = self.validate(spectrum, seed=int(rng.integers(1 << 31)))
79
+ results.append((n, result))
80
+ return results