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,53 @@
1
+ """Marchenko-Pastur law benchmark for the Wishart (Laguerre) ensembles.
2
+
3
+ Reference: V. A. Marchenko, L. A. Pastur, Mat. Sb. 72 (1967) 507.
4
+ """
5
+
6
+ import numpy as np
7
+ from scipy.interpolate import interp1d
8
+
9
+ from ..stats.marchenko_pastur import mp_pdf, mp_support
10
+ from .base import Benchmark
11
+
12
+
13
+ class MarchenkoPastur(Benchmark):
14
+ """Validates the empirical spectral density of a Wishart ensemble
15
+ (LOE/LUE/LSE, or general beta-Laguerre) against the exact
16
+ Marchenko-Pastur law for a given aspect ratio gamma = n/m.
17
+
18
+ CDF and its inverse are built once per (gamma, sigma2) via a fine-grid
19
+ cumulative trapezoidal integration and cached as interpolators, for
20
+ the same reason as ``physicskit.rmt.stats.ratios.RatioSurmise``: a KS test
21
+ evaluates the CDF at every data point, so per-point ``quad`` calls
22
+ would be far too slow.
23
+ """
24
+
25
+ def __init__(self, gamma: float, sigma2: float = 1.0, grid_size: int = 4000) -> None:
26
+ if not (0 < gamma <= 1):
27
+ raise ValueError("gamma must be in (0, 1] for this ensemble convention (m >= n samples/variables, no point mass at zero)")
28
+ self.gamma = gamma
29
+ self.sigma2 = sigma2
30
+ self._lo, self._hi = mp_support(gamma, sigma2)
31
+
32
+ from scipy.integrate import cumulative_trapezoid
33
+
34
+ grid = np.linspace(self._lo, self._hi, grid_size)
35
+ pdf_vals = mp_pdf(grid, gamma, sigma2)
36
+ cdf_vals = cumulative_trapezoid(pdf_vals, grid, initial=0.0)
37
+ cdf_vals /= cdf_vals[-1] # correct for edge-integrable-singularity discretization error
38
+ self._grid = grid
39
+ self._pdf_vals = pdf_vals
40
+ self._cdf_interp = interp1d(grid, cdf_vals, bounds_error=False, fill_value=(0.0, 1.0))
41
+ self._inv_cdf_interp = interp1d(cdf_vals, grid, bounds_error=False, fill_value=(self._lo, self._hi))
42
+
43
+ def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
44
+ return mp_pdf(x, self.gamma, self.sigma2)
45
+
46
+ def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
47
+ x = np.asarray(x, dtype=float)
48
+ out = np.where(x <= self._lo, 0.0, np.where(x >= self._hi, 1.0, self._cdf_interp(x)))
49
+ return out
50
+
51
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
52
+ u = rng.uniform(0.0, 1.0, size=size)
53
+ return self._inv_cdf_interp(u)
@@ -0,0 +1,53 @@
1
+ """Benchmark for the consecutive-spacing ratio distribution.
2
+
3
+ Reference: Y. Y. Atas, E. Bogomolny, O. Giraud, G. Roux, "Distribution of
4
+ the Ratio of Consecutive Level Spacings in Random Matrix Ensembles",
5
+ Phys. Rev. Lett. 110, 084101 (2013).
6
+
7
+ Deliberately structured as an independent check from ``WignerSurmise``:
8
+ the ratio statistic needs no unfolding, so agreement here rules out
9
+ unfolding artifacts as the explanation for agreement with the
10
+ (unfolding-dependent) spacing distribution.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import numpy as np
16
+ from scipy.stats import kstest, wasserstein_distance
17
+
18
+ from ..stats.ratios import RatioSurmise as _RatioSurmiseModel
19
+ from .base import ValidationResult
20
+
21
+
22
+ class RatioDistribution:
23
+ """Validates a ratio-statistic sample against the Atas et al. (2013)
24
+ surmise at a given Dyson index beta."""
25
+
26
+ reference_size: int = 200_000
27
+
28
+ def __init__(self, beta: float) -> None:
29
+ self.beta = beta
30
+ self._model = _RatioSurmiseModel(beta)
31
+
32
+ def theoretical_pdf(self, r: np.ndarray) -> np.ndarray:
33
+ return self._model.pdf(r)
34
+
35
+ def theoretical_cdf(self, r: np.ndarray) -> np.ndarray:
36
+ return self._model.cdf(r)
37
+
38
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
39
+ return self._model.rvs(size, rng)
40
+
41
+ def validate(self, ratios: np.ndarray, seed: int | np.random.Generator | None = None) -> ValidationResult:
42
+ """Compare an array of ratio statistics (each in [0, 1], from
43
+ ``physicskit.rmt.stats.ratios.ratio_statistics``) against the surmise."""
44
+ ks = kstest(ratios, self.theoretical_cdf)
45
+ rng = np.random.default_rng(seed)
46
+ reference = self.reference_samples(self.reference_size, rng)
47
+ wd = wasserstein_distance(ratios, reference)
48
+ return ValidationResult(
49
+ ks_statistic=float(ks.statistic),
50
+ ks_pvalue=float(ks.pvalue),
51
+ wasserstein_distance=float(wd),
52
+ n_eigenvalues=len(ratios),
53
+ )
@@ -0,0 +1,60 @@
1
+ """Real-Ginibre real-eigenvalue-count benchmark.
2
+
3
+ Like ``physicskit.rmt.validation.sine_kernel.SineKernel``, this doesn't fit the
4
+ KS/Wasserstein ``Benchmark`` interface: it compares a scalar expected
5
+ COUNT (of real eigenvalues), not a sample against a 1-D distribution. It
6
+ gets its own lightweight result type instead.
7
+
8
+ Reference: A. Edelman, E. Kostlan, S. Shub, J. Amer. Math. Soc. 7 (1994)
9
+ 247. See ``physicskit.rmt.stats.real_ginibre`` for the precision caveat: the
10
+ theoretical value used here is the verified large-n asymptotic
11
+ expansion, not the exact finite-n Edelman-Kostlan-Shub closed form.
12
+ """
13
+
14
+ from dataclasses import dataclass
15
+
16
+ from ..spectrum import Spectrum
17
+ from ..stats.real_ginibre import real_eigenvalue_count_asymptotic, real_eigenvalue_count_empirical
18
+
19
+
20
+ @dataclass
21
+ class RealEigenvalueCountResult:
22
+ """Result of comparing a GinOE Spectrum's empirical mean real-
23
+ eigenvalue count against the asymptotic theoretical value."""
24
+
25
+ empirical_mean: float
26
+ theoretical: float
27
+ n: int
28
+
29
+ @property
30
+ def relative_error(self) -> float:
31
+ return abs(self.empirical_mean - self.theoretical) / self.theoretical
32
+
33
+ def __repr__(self) -> str:
34
+ return (
35
+ f"RealEigenvalueCountResult(empirical_mean={self.empirical_mean:.4f}, "
36
+ f"theoretical={self.theoretical:.4f}, n={self.n}, "
37
+ f"relative_error={self.relative_error:.4f})"
38
+ )
39
+
40
+
41
+ class RealGinibreEigenvalueCount:
42
+ """Validates a real-Ginibre (``GinOE``) Spectrum's mean number of
43
+ real eigenvalues per sample against the asymptotic expected count
44
+ sqrt(2n/pi) + 1/2 (see ``physicskit.rmt.stats.real_ginibre`` for the
45
+ asymptotic-vs-exact precision caveat)."""
46
+
47
+ def __init__(self, tol: float = 1e-8) -> None:
48
+ """
49
+ Parameters
50
+ ----------
51
+ tol : float, optional
52
+ Passed to ``real_eigenvalue_count_empirical``: an eigenvalue
53
+ counts as real if ``abs(Im(lambda))`` is below this.
54
+ """
55
+ self.tol = tol
56
+
57
+ def validate(self, spectrum: Spectrum) -> RealEigenvalueCountResult:
58
+ counts = real_eigenvalue_count_empirical(spectrum.eigenvalues, tol=self.tol)
59
+ theoretical = real_eigenvalue_count_asymptotic(spectrum.n)
60
+ return RealEigenvalueCountResult(empirical_mean=float(counts.mean()), theoretical=theoretical, n=spectrum.n)
@@ -0,0 +1,57 @@
1
+ """Sine-kernel benchmark for the Circular Unitary Ensemble's bulk
2
+ two-point correlation function.
3
+
4
+ This does not fit the KS/Wasserstein ``Benchmark`` interface used
5
+ elsewhere in the package: it validates a *correlation function* (a
6
+ density estimated by pair-counting), not a sample from a 1-D
7
+ distribution. It gets its own lightweight result type instead.
8
+
9
+ Reference: F. J. Dyson, J. Math. Phys. 3 (1962) 140, 157, 166.
10
+ """
11
+
12
+ from dataclasses import dataclass
13
+
14
+ import numpy as np
15
+
16
+ from ..spectrum import Spectrum
17
+ from ..stats.correlations import pair_correlation_estimate, sine_kernel_r2
18
+
19
+
20
+ @dataclass
21
+ class CorrelationValidationResult:
22
+ rmse: float
23
+ r_max: float
24
+ n_bins: int
25
+
26
+ def __repr__(self) -> str:
27
+ return f"CorrelationValidationResult(rmse={self.rmse:.5f}, r_max={self.r_max}, n_bins={self.n_bins})"
28
+
29
+
30
+ class SineKernel:
31
+ """Validates a CUE (beta=2) Spectrum's bulk two-point correlation
32
+ function against the exact determinantal sine-kernel result.
33
+
34
+ Only beta=2 is implemented -- see module docstring in
35
+ ``physicskit.rmt.stats.correlations`` for why the beta=1, 4 cases (COE, CSE)
36
+ are deferred rather than guessed at.
37
+ """
38
+
39
+ def __init__(self, r_max: float = 4.0, n_bins: int = 60) -> None:
40
+ self.r_max = r_max
41
+ self.n_bins = n_bins
42
+
43
+ def theoretical_r2(self, r: np.ndarray) -> np.ndarray:
44
+ return sine_kernel_r2(r)
45
+
46
+ def validate(self, spectrum: Spectrum) -> CorrelationValidationResult:
47
+ if spectrum.beta != 2:
48
+ raise ValueError(
49
+ "SineKernel currently only validates beta=2 (CUE) spectra; "
50
+ "the beta=1 (COE) and beta=4 (CSE) correlation kernels "
51
+ "require the fuller Pfaffian point-process machinery, "
52
+ "not yet implemented (see physicskit.rmt.stats.correlations)."
53
+ )
54
+ centers, estimate = pair_correlation_estimate(spectrum, r_max=self.r_max, n_bins=self.n_bins)
55
+ theory = self.theoretical_r2(centers)
56
+ rmse = float(np.sqrt(np.mean((estimate - theory) ** 2)))
57
+ return CorrelationValidationResult(rmse=rmse, r_max=self.r_max, n_bins=self.n_bins)
@@ -0,0 +1,68 @@
1
+ """Single ring theorem benchmark for bi-unitarily-invariant non-Hermitian
2
+ ensembles.
3
+
4
+ References: Feinberg-Zee (1997); Guionnet-Krishnapur-Zeitouni (2011) --
5
+ see ``physicskit.rmt.ensembles.single_ring`` for the full construction and
6
+ precision notes.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import numpy as np
12
+
13
+ from ..spectrum import Spectrum
14
+ from ..stats.single_ring import annulus_radial_cdf, annulus_radial_pdf
15
+ from .base import Benchmark, ValidationResult
16
+
17
+
18
+ class SingleRingTheorem(Benchmark):
19
+ """Validates a bi-unitarily-invariant non-Hermitian Spectrum's
20
+ eigenvalue *radii* against the single ring theorem's radial marginal
21
+ (uniform on the annulus ``r_in <= |z| <= r_out``).
22
+
23
+ Like ``CircularLaw`` (its r_in=0 special case), overrides
24
+ ``validate`` because the base class compares ``spectrum.rescaled``
25
+ directly, but here it's the eigenvalue *magnitudes* that are
26
+ compared to a 1-D theoretical distribution.
27
+
28
+ Parameters
29
+ ----------
30
+ r_in : float
31
+ r_out : float
32
+ The theoretical ring radii (see
33
+ ``physicskit.rmt.stats.single_ring.single_ring_radii`` /
34
+ ``single_ring_radii_wishart_theory``).
35
+ """
36
+
37
+ def __init__(self, r_in: float, r_out: float) -> None:
38
+ if not 0.0 <= r_in < r_out:
39
+ raise ValueError(f"require 0 <= r_in < r_out, got r_in={r_in}, r_out={r_out}")
40
+ self.r_in = r_in
41
+ self.r_out = r_out
42
+
43
+ def theoretical_pdf(self, r: np.ndarray) -> np.ndarray:
44
+ return annulus_radial_pdf(r, self.r_in, self.r_out)
45
+
46
+ def theoretical_cdf(self, r: np.ndarray) -> np.ndarray:
47
+ return annulus_radial_cdf(r, self.r_in, self.r_out)
48
+
49
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
50
+ # Inverse-CDF sampling: F(r) = (r^2-r_in^2)/(r_out^2-r_in^2)
51
+ # => r = sqrt(r_in^2 + U*(r_out^2-r_in^2)), U ~ Uniform(0,1)
52
+ u = rng.uniform(0.0, 1.0, size=size)
53
+ return np.sqrt(self.r_in**2 + u * (self.r_out**2 - self.r_in**2))
54
+
55
+ def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
56
+ from scipy.stats import kstest, wasserstein_distance
57
+
58
+ radii = np.abs(spectrum.rescaled.ravel())
59
+ ks = kstest(radii, self.theoretical_cdf)
60
+ rng = np.random.default_rng(seed)
61
+ reference = self.reference_samples(self.reference_size, rng)
62
+ wd = wasserstein_distance(radii, reference)
63
+ return ValidationResult(
64
+ ks_statistic=float(ks.statistic),
65
+ ks_pvalue=float(ks.pvalue),
66
+ wasserstein_distance=float(wd),
67
+ n_eigenvalues=len(radii),
68
+ )
@@ -0,0 +1,89 @@
1
+ """Tracy-Widom soft-edge benchmark for the Gaussian ensembles.
2
+
3
+ References: C. A. Tracy, H. Widom, Commun. Math. Phys. 159 (1994) 151;
4
+ Commun. Math. Phys. 177 (1996) 727.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Callable
10
+
11
+ import numpy as np
12
+ from scipy.stats import kstest, wasserstein_distance
13
+
14
+ from ..ensembles.base import MatrixEnsemble
15
+ from ..spectrum import Spectrum
16
+ from ..stats.tracy_widom import (
17
+ largest_eigenvalues,
18
+ tracy_widom_cdf,
19
+ tracy_widom_edge_scale,
20
+ tracy_widom_rvs,
21
+ )
22
+ from .base import ValidationResult
23
+
24
+
25
+ class TracyWidom:
26
+ """Validates the largest-eigenvalue statistic of a Gaussian-ensemble
27
+ Spectrum against the Tracy-Widom soft-edge law at the matching beta.
28
+
29
+ Only beta in {1, 2, 4} are supported (F_1, F_2, F_4 have closed
30
+ forms; general beta does not -- see ``physicskit.rmt.stats.tracy_widom``).
31
+ Overrides ``validate`` (rather than just providing
32
+ ``theoretical_cdf``/``reference_samples``) because the relevant
33
+ statistic is the *largest* eigenvalue after a beta-dependent edge
34
+ rescaling -- not the full ``spectrum.rescaled`` array the base class
35
+ compares directly.
36
+ """
37
+
38
+ reference_size: int = 200_000
39
+
40
+ def __init__(self, beta: int) -> None:
41
+ if beta not in (1, 2, 4):
42
+ raise ValueError(f"TracyWidom only supports beta in (1, 2, 4), got {beta}")
43
+ self.beta = beta
44
+
45
+ def theoretical_cdf(self, s: np.ndarray) -> np.ndarray:
46
+ return tracy_widom_cdf(s, self.beta)
47
+
48
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
49
+ return tracy_widom_rvs(size, self.beta, rng)
50
+
51
+ def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
52
+ if spectrum.beta != self.beta:
53
+ raise ValueError(f"Spectrum beta ({spectrum.beta}) does not match TracyWidom benchmark beta ({self.beta})")
54
+ lam_max = largest_eigenvalues(spectrum)
55
+ scale = tracy_widom_edge_scale(spectrum.n, self.beta)
56
+ edge_stat = scale * (lam_max - 2.0)
57
+
58
+ ks = kstest(edge_stat, self.theoretical_cdf)
59
+ rng = np.random.default_rng(seed)
60
+ reference = self.reference_samples(self.reference_size, rng)
61
+ wd = wasserstein_distance(edge_stat, reference)
62
+ return ValidationResult(
63
+ ks_statistic=float(ks.statistic),
64
+ ks_pvalue=float(ks.pvalue),
65
+ wasserstein_distance=float(wd),
66
+ n_eigenvalues=len(edge_stat),
67
+ )
68
+
69
+ def convergence_curve(
70
+ self,
71
+ ensemble_factory: Callable[..., MatrixEnsemble],
72
+ n_values: list[int],
73
+ n_samples: int = 200,
74
+ seed: int | np.random.Generator | None = None,
75
+ ) -> list[tuple[int, ValidationResult]]:
76
+ """Convergence to Tracy-Widom is notoriously slow (the largest
77
+ eigenvalue is a single extreme-value statistic per sample, so
78
+ each matrix contributes only one data point -- n_samples needs
79
+ to be much larger than in the bulk-statistic benchmarks to get
80
+ a stable KS estimate at all). Expect a shallower, noisier trend
81
+ than the semicircle/Marchenko-Pastur convergence curves."""
82
+ rng = np.random.default_rng(seed)
83
+ results = []
84
+ for n in n_values:
85
+ ensemble = ensemble_factory(n, seed=int(rng.integers(1 << 31)))
86
+ spectrum = ensemble.sample(n_samples=n_samples)
87
+ result = self.validate(spectrum, seed=int(rng.integers(1 << 31)))
88
+ results.append((n, result))
89
+ return results
@@ -0,0 +1,100 @@
1
+ """Universality benchmark: confirms that the semicircle law (and,
2
+ optionally, the Wigner surmise) hold across several different entry
3
+ distributions, not just Gaussian ones -- a stronger validation of "the
4
+ theory is implemented correctly" than fitting a single Gaussian case
5
+ ever could be, since it tests the actual universality *claim* of RMT
6
+ rather than one specific realization of it.
7
+
8
+ This module ties together ``physicskit.rmt.ensembles.universality`` (the
9
+ entry-distribution-agnostic ensemble) and the already-existing
10
+ ``WignerSemicircle`` / ``WignerSurmise`` benchmarks -- no new
11
+ theoretical curve is needed here, since universality means the SAME
12
+ curve applies regardless of entry distribution.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections.abc import Callable
18
+ from dataclasses import dataclass, field
19
+
20
+ import numpy as np
21
+
22
+ from ..ensembles.universality import (
23
+ GeneralWignerEnsemble,
24
+ exponential_centered_unit_variance,
25
+ rademacher,
26
+ uniform_unit_variance,
27
+ )
28
+ from .base import ValidationResult
29
+ from .wigner_semicircle import WignerSemicircle
30
+
31
+ EntrySampler = Callable[[np.random.Generator, tuple[int, ...]], np.ndarray]
32
+
33
+ #: A representative, deliberately varied set of entry distributions:
34
+ #: symmetric-bounded (uniform), symmetric-discrete (Rademacher), and
35
+ #: asymmetric-unbounded (centered exponential) -- if universality holds
36
+ #: across all three, entry-distribution shape/boundedness/symmetry are
37
+ #: all ruled out as confounds.
38
+ DEFAULT_ENTRY_DISTRIBUTIONS: dict[str, EntrySampler] = {
39
+ "uniform": uniform_unit_variance,
40
+ "rademacher": rademacher,
41
+ "exponential": exponential_centered_unit_variance,
42
+ }
43
+
44
+
45
+ @dataclass
46
+ class UniversalityResult:
47
+ """Per-distribution validation results plus a summary judgment."""
48
+
49
+ per_distribution: dict[str, ValidationResult] = field(default_factory=dict)
50
+ max_ks_statistic: float = 0.0
51
+
52
+ def __repr__(self):
53
+ lines = [f" {name}: {result}" for name, result in self.per_distribution.items()]
54
+ return "UniversalityResult(\n" + "\n".join(lines) + f"\n max_ks={self.max_ks_statistic:.5f}\n)"
55
+
56
+
57
+ def check_universality(
58
+ n: int,
59
+ beta: int,
60
+ n_samples: int = 25,
61
+ seed: int | None = None,
62
+ entry_distributions: dict[str, EntrySampler] | None = None,
63
+ ks_threshold: float = 0.02,
64
+ ) -> UniversalityResult:
65
+ """Sample ``GeneralWignerEnsemble`` with several entry distributions
66
+ at the given n and beta, validate each against the Wigner semicircle
67
+ law, and return per-distribution results plus a pass/fail summary.
68
+
69
+ Parameters
70
+ ----------
71
+ n : int
72
+ beta : int
73
+ 1 or 2 (see ``GeneralWignerEnsemble``).
74
+ n_samples : int
75
+ seed : int or None
76
+ entry_distributions : dict or None
77
+ name -> entry_sampler callable; defaults to
78
+ ``DEFAULT_ENTRY_DISTRIBUTIONS``.
79
+ ks_threshold : float
80
+ Each distribution's KS statistic against the semicircle law
81
+ must be below this for the overall check to pass.
82
+
83
+ Returns
84
+ -------
85
+ UniversalityResult
86
+ """
87
+ if entry_distributions is None:
88
+ entry_distributions = DEFAULT_ENTRY_DISTRIBUTIONS
89
+
90
+ benchmark = WignerSemicircle()
91
+ per_distribution = {}
92
+ for i, (name, sampler) in enumerate(entry_distributions.items()):
93
+ ensemble_seed = None if seed is None else seed + i
94
+ ensemble = GeneralWignerEnsemble(n=n, entry_sampler=sampler, beta=beta, seed=ensemble_seed)
95
+ spectrum = ensemble.sample(n_samples=n_samples)
96
+ result = benchmark.validate(spectrum, seed=ensemble_seed)
97
+ per_distribution[name] = result
98
+
99
+ max_ks = max(r.ks_statistic for r in per_distribution.values())
100
+ return UniversalityResult(per_distribution=per_distribution, max_ks_statistic=max_ks)
@@ -0,0 +1,52 @@
1
+ """Wachter distribution benchmark for the Jacobi (MANOVA) ensembles.
2
+
3
+ Reference: K. W. Wachter, Ann. Statist. 8 (1980) 937.
4
+ """
5
+
6
+ import numpy as np
7
+ from scipy.integrate import cumulative_trapezoid
8
+ from scipy.interpolate import interp1d
9
+
10
+ from ..stats.wachter import wachter_pdf, wachter_support
11
+ from .base import Benchmark
12
+
13
+
14
+ class Wachter(Benchmark):
15
+ """Validates the empirical spectral density of a Jacobi ensemble
16
+ (JOE/JUE) against the exact Wachter law for given inverse aspect
17
+ ratios a = m1/n, b = m2/n.
18
+
19
+ CDF and its inverse are built once via a fine-grid cumulative
20
+ trapezoidal integration and cached as interpolators, for the same
21
+ reason as ``physicskit.rmt.validation.marchenko_pastur.MarchenkoPastur``: a
22
+ KS test evaluates the CDF at every data point, so per-point
23
+ ``quad`` calls (as in ``physicskit.rmt.stats.wachter.wachter_cdf``) would be
24
+ far too slow.
25
+ """
26
+
27
+ def __init__(self, a: float, b: float, grid_size: int = 4000) -> None:
28
+ if a < 1 or b < 1:
29
+ raise ValueError(f"a and b are inverse aspect ratios (m/n) and must be >= 1; got a={a}, b={b}")
30
+ self.a = a
31
+ self.b = b
32
+ self._lo, self._hi = wachter_support(a, b)
33
+
34
+ grid = np.linspace(self._lo, self._hi, grid_size)
35
+ pdf_vals = wachter_pdf(grid, a, b)
36
+ cdf_vals = cumulative_trapezoid(pdf_vals, grid, initial=0.0)
37
+ cdf_vals /= cdf_vals[-1]
38
+ self._grid = grid
39
+ self._pdf_vals = pdf_vals
40
+ self._cdf_interp = interp1d(grid, cdf_vals, bounds_error=False, fill_value=(0.0, 1.0))
41
+ self._inv_cdf_interp = interp1d(cdf_vals, grid, bounds_error=False, fill_value=(self._lo, self._hi))
42
+
43
+ def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
44
+ return wachter_pdf(x, self.a, self.b)
45
+
46
+ def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
47
+ x = np.asarray(x, dtype=float)
48
+ return np.where(x <= self._lo, 0.0, np.where(x >= self._hi, 1.0, self._cdf_interp(x)))
49
+
50
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
51
+ u = rng.uniform(0.0, 1.0, size=size)
52
+ return self._inv_cdf_interp(u)
@@ -0,0 +1,40 @@
1
+ """Wigner semicircle law benchmark.
2
+
3
+ References
4
+ ----------
5
+ E. Wigner, "Characteristic Vectors of Bordered Matrices With Infinite
6
+ Dimensions", Ann. Math. 62 (1955) 548.
7
+ E. Wigner, "On the Distribution of the Roots of Certain Symmetric
8
+ Matrices", Ann. Math. 67 (1958) 325.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.stats import beta as beta_dist
13
+
14
+ from ..stats.density import semicircle_cdf, semicircle_pdf
15
+ from .base import Benchmark
16
+
17
+
18
+ class WignerSemicircle(Benchmark):
19
+ """Validates the empirical spectral density of a Gaussian
20
+ beta-ensemble (GOE/GUE/GSE, or general beta-Hermite) against the
21
+ exact Wigner semicircle law.
22
+
23
+ The semicircle distribution on [-radius, radius] is an affine
24
+ transform of Beta(3/2, 3/2): if U ~ Beta(3/2, 3/2), then
25
+ X = radius * (2U - 1) has semicircle density, which is how
26
+ ``reference_samples`` draws from the theoretical law.
27
+ """
28
+
29
+ def __init__(self, radius: float = 2.0) -> None:
30
+ self.radius = radius
31
+
32
+ def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
33
+ return semicircle_cdf(x, radius=self.radius)
34
+
35
+ def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
36
+ return semicircle_pdf(x, radius=self.radius)
37
+
38
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
39
+ u = beta_dist.rvs(1.5, 1.5, size=size, random_state=rng)
40
+ return self.radius * (2.0 * u - 1.0)
@@ -0,0 +1,58 @@
1
+ """Benchmark for the (generalized) Wigner surmise -- the nearest-neighbor
2
+ spacing distribution of the Gaussian ensembles.
3
+
4
+ Unlike ``WignerSemicircle``, this benchmark validates a *derived*
5
+ statistic (unfolded spacings), so ``validate`` here expects pre-computed
6
+ spacings (from ``physicskit.rmt.stats.spacing.nearest_neighbor_spacings``), not a
7
+ raw Spectrum -- unfolding and edge-trimming choices belong to the caller,
8
+ not baked into the benchmark.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import numpy as np
14
+ from scipy.stats import kstest, wasserstein_distance
15
+
16
+ from ..stats.spacing import (
17
+ wigner_surmise_cdf,
18
+ wigner_surmise_pdf,
19
+ wigner_surmise_samples,
20
+ )
21
+ from .base import ValidationResult
22
+
23
+
24
+ class WignerSurmise:
25
+ """Validates a spacing sample against the generalized Wigner surmise
26
+ at a given Dyson index beta.
27
+
28
+ References: Wigner's original surmise (beta=1); Mehta, *Random
29
+ Matrices* (3rd ed.), 2004, for the beta=2, 4 cases.
30
+ """
31
+
32
+ reference_size: int = 200_000
33
+
34
+ def __init__(self, beta: float) -> None:
35
+ self.beta = beta
36
+
37
+ def theoretical_pdf(self, s: np.ndarray) -> np.ndarray:
38
+ return wigner_surmise_pdf(s, self.beta)
39
+
40
+ def theoretical_cdf(self, s: np.ndarray) -> np.ndarray:
41
+ return wigner_surmise_cdf(s, self.beta)
42
+
43
+ def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
44
+ return wigner_surmise_samples(size, self.beta, rng)
45
+
46
+ def validate(self, spacings: np.ndarray, seed: int | np.random.Generator | None = None) -> ValidationResult:
47
+ """Compare an array of (already unfolded, already edge-trimmed)
48
+ spacings against the surmise."""
49
+ ks = kstest(spacings, self.theoretical_cdf)
50
+ rng = np.random.default_rng(seed)
51
+ reference = self.reference_samples(self.reference_size, rng)
52
+ wd = wasserstein_distance(spacings, reference)
53
+ return ValidationResult(
54
+ ks_statistic=float(ks.statistic),
55
+ ks_pvalue=float(ks.pvalue),
56
+ wasserstein_distance=float(wd),
57
+ n_eigenvalues=len(spacings),
58
+ )