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,92 @@
1
+ """Random graph Laplacians -- spectral graph theory / network transport
2
+ ensembles, built on an Erdos-Renyi G(n, p) random graph.
3
+
4
+ References
5
+ ----------
6
+ F. R. K. Chung, "Spectral Graph Theory", CBMS Regional Conference
7
+ Series in Mathematics 92, AMS, 1997 -- normalized Laplacian eigenvalues
8
+ lie in [0, 2] for any graph, with 2 attained iff a connected component
9
+ is bipartite (used below as ``natural_scale`` for the normalized case).
10
+ F. Chung, L. Lu, V. Vu, "Spectra of random graphs with given expected
11
+ degrees", Proc. Natl. Acad. Sci. 100 (2003) 6313 -- Erdos-Renyi Laplacian
12
+ spectral asymptotics.
13
+
14
+ Construction: an n x n symmetric 0/1 adjacency matrix A with i.i.d.
15
+ Bernoulli(p) entries above the diagonal (zero diagonal -- no self-loops),
16
+ mirrored below. Two Laplacian variants:
17
+
18
+ combinatorial: L = D - A (D = diag(degree))
19
+ normalized: L_sym = I - D^(-1/2) A D^(-1/2)
20
+
21
+ Isolated vertices (degree 0, possible whenever p is small enough) are
22
+ handled by convention: an isolated vertex contributes an exact zero row/
23
+ column to A, hence an exact zero eigenvalue to L trivially, and is
24
+ assigned D^(-1/2) = 0 for that vertex in L_sym (rather than raising a
25
+ division error) -- the standard spectral-graph-theory convention,
26
+ matching how an isolated vertex trivially decouples from graph
27
+ transport dynamics either way.
28
+
29
+ Unlike the semicircle/Marchenko-Pastur ensembles, the COMBINATORIAL
30
+ Laplacian's spectrum is not naturally centered at 0 (it concentrates
31
+ around the mean degree n*p for large n*p, since L = D - A and D
32
+ concentrates around n*p -- Chung-Lu-Vu), so ``natural_scale`` performs
33
+ no rescaling for that case (dividing by a single scalar cannot both
34
+ correctly center and rescale it); the NORMALIZED Laplacian, by contrast,
35
+ has an exact, graph-independent bound (eigenvalues in [0, 2]), so no
36
+ rescaling is needed there either, for the opposite reason.
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ import numpy as np
42
+
43
+ from .base import MatrixEnsemble
44
+
45
+
46
+ def _erdos_renyi_adjacency(n: int, p: float, rng: np.random.Generator) -> np.ndarray:
47
+ upper = np.triu(rng.random((n, n)) < p, k=1)
48
+ return upper.astype(float) + upper.T.astype(float)
49
+
50
+
51
+ class GraphLaplacianEnsemble(MatrixEnsemble):
52
+ """Laplacian eigenvalues of an Erdos-Renyi G(n, p) random graph.
53
+
54
+ Parameters
55
+ ----------
56
+ n : int
57
+ Number of vertices.
58
+ p : float
59
+ Edge probability, in (0, 1].
60
+ normalized : bool, optional
61
+ If True (default False), use the normalized Laplacian
62
+ I - D^(-1/2) A D^(-1/2) (eigenvalues in [0, 2]) instead of the
63
+ combinatorial Laplacian D - A.
64
+ """
65
+
66
+ def __init__(
67
+ self,
68
+ n: int,
69
+ p: float,
70
+ normalized: bool = False,
71
+ seed: int | np.random.Generator | None = None,
72
+ ) -> None:
73
+ if not 0.0 < p <= 1.0:
74
+ raise ValueError(f"p must be in (0, 1], got {p}")
75
+ super().__init__(n, seed=seed)
76
+ self.p = float(p)
77
+ self.normalized = normalized
78
+
79
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
80
+ a = _erdos_renyi_adjacency(self.n, self.p, rng)
81
+ degree = a.sum(axis=1)
82
+ if self.normalized:
83
+ with np.errstate(divide="ignore"):
84
+ d_inv_sqrt = np.where(degree > 0, 1.0 / np.sqrt(degree), 0.0)
85
+ laplacian = np.eye(self.n) - (d_inv_sqrt[:, None] * a) * d_inv_sqrt[None, :]
86
+ else:
87
+ laplacian = np.diag(degree) - a
88
+ laplacian = (laplacian + laplacian.T) / 2.0
89
+ return np.linalg.eigvalsh(laplacian)
90
+
91
+ def natural_scale(self) -> float:
92
+ return 1.0
@@ -0,0 +1,57 @@
1
+ """Eigenvalue statistics of a raw Haar-random orthogonal matrix --
2
+ deliberately distinct from the Circular Orthogonal Ensemble.
3
+
4
+ Reference: F. Mezzadri, "How to generate random matrices from the
5
+ classical compact groups", Notices Amer. Math. Soc. 54 (2007) 592.
6
+
7
+ As already flagged in ``physicskit.rmt.ensembles.circular``'s module docstring:
8
+ COE is NOT "eigenvalues of a Haar-random orthogonal matrix" -- a real
9
+ orthogonal matrix's eigenvalues are either real (+-1) or complex-
10
+ conjugate pairs on the unit circle, not the generic uniform-phase
11
+ structure a circular ensemble requires. This module provides that
12
+ other, genuinely different object directly, rather than leaving it as
13
+ only a cautionary note: the raw-matrix generators themselves
14
+ (``haar_orthogonal``, ``haar_unitary``, ``haar_symplectic``) live in
15
+ ``physicskit.rmt.utils.haar``, since what they return is the group element
16
+ itself (useful directly as a random quantum gate / change of basis for
17
+ circuit-simulation and randomized-benchmarking use cases) rather than
18
+ an eigenvalue-only product -- the one architectural mismatch with every
19
+ other ensemble in this package, whose ``.sample()`` is built around
20
+ eigenvalues as the primary product (see ``physicskit.rmt.ensembles.base``).
21
+ ``HaarUnitary``'s and ``HaarSymplectic``'s eigenvalue-phase statistics
22
+ are, respectively, already exactly what CUE and CSE model (see
23
+ ``physicskit.rmt.ensembles.circular``), so no separate ensemble classes are
24
+ added here for those -- only the genuinely new, non-duplicate
25
+ Haar-orthogonal eigenvalue ensemble below.
26
+
27
+ Verified during development (see ``tests/test_haar.py``): unlike CUE
28
+ (zero real eigenvalues, generically) and COE (also generically no real
29
+ eigenvalues -- see ``physicskit.rmt.ensembles.circular``), a raw Haar-O(n)
30
+ matrix has a genuinely nonzero real-eigenvalue count whose expectation
31
+ stays O(1) (order unity, empirically close to 1) rather than vanishing
32
+ or growing, as n increases -- structurally distinct from both the
33
+ circular-ensemble and the GinOE real-eigenvalue-count stories (the
34
+ latter grows like sqrt(2n/pi), Edelman-Kostlan-Shub 1994).
35
+ """
36
+
37
+ import numpy as np
38
+
39
+ from ..utils.haar import haar_orthogonal
40
+ from .base import MatrixEnsemble
41
+
42
+
43
+ class HaarOrthogonalEnsemble(MatrixEnsemble):
44
+ """Eigenvalues of a raw n x n Haar-random orthogonal matrix (O(n)).
45
+
46
+ ``Spectrum.eigenvalues`` holds n complex values per sample (a mix of
47
+ real +-1 eigenvalues and complex-conjugate pairs on the unit
48
+ circle) -- see module docstring for why this is a distinct object
49
+ from :class:`~physicskit.rmt.ensembles.circular.COE`.
50
+ """
51
+
52
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
53
+ o = haar_orthogonal(self.n, rng)
54
+ return np.linalg.eigvals(o)
55
+
56
+ def natural_scale(self) -> float:
57
+ return 1.0
@@ -0,0 +1,182 @@
1
+ """Jacobi (MANOVA) ensembles -- JOE (beta=1), JUE (beta=2).
2
+
3
+ References
4
+ ----------
5
+ K. W. Wachter, "The limiting empirical measure of multiple discriminant
6
+ ratios", Ann. Statist. 8 (1980) 937 -- the limiting law (see
7
+ ``physicskit.rmt.stats.wachter``).
8
+ L. Erdos, B. Farrell, "Local Eigenvalue Density for General MANOVA
9
+ Matrices" (arXiv:1207.0031) -- the construction and formula used here,
10
+ verified numerically during development (see design notes and
11
+ ``tests/test_jacobi_wachter.py``).
12
+
13
+ Construction: given two independent Wishart-type matrices A, B (n x n,
14
+ built from m1 and m2 "samples" respectively, m1, m2 >= n), the Jacobi
15
+ ensemble matrix is
16
+
17
+ J = A (A + B)^{-1}
18
+
19
+ Unlike Gaussian/Wishart, this requires the actual dense matrices A and B
20
+ (not just their eigenvalues), since the ratio construction mixes them --
21
+ none of the tridiagonal/bidiagonal eigenvalue-only tricks used elsewhere
22
+ in this package apply here. J itself is generally NOT symmetric, but is
23
+ similar to the manifestly symmetric matrix
24
+
25
+ M = (A+B)^{-1/2} A (A+B)^{-1/2}
26
+
27
+ (via conjugation by (A+B)^{1/2}), which has the same eigenvalues and is
28
+ what's actually diagonalized here for numerical stability. Eigenvalues
29
+ are real and confined to [0, 1] (a "double hard edge", unlike the
30
+ semicircle/Marchenko-Pastur soft edges).
31
+
32
+ Parametrization: following Erdos-Farrell/Wachter, a = m1/n, b = m2/n
33
+ (inverse aspect ratios, i.e. "samples per variable" -- note this is the
34
+ reciprocal of the gamma = n/m convention used in
35
+ ``physicskit.rmt.ensembles.wishart``).
36
+
37
+ beta=4 (quaternion, "JSE"): A and B are built from n x m quaternion
38
+ matrices, embedded as 2n x 2m complex matrices via the standard
39
+ quaternion-to-2x2-complex-block map (the same convention as
40
+ ``physicskit.rmt.ensembles.ginibre.GinSE`` and
41
+ ``physicskit.rmt.ensembles.effective_hamiltonian``'s beta=4 case). Since that
42
+ embedding is an algebra homomorphism, A, B, S = A+B, and
43
+ M = S^{-1/2} A S^{-1/2} are all quaternionic self-dual Hermitian, so M's
44
+ 2n eigenvalues come in exact Kramers double-degenerate pairs -- verified
45
+ numerically during development to machine precision (~1e-15) before
46
+ being trusted here, the same true degeneracy GSE/CSE/EffGSE have (as
47
+ opposed to GinSE's merely-conjugate, genuinely distinct pairs). Only the
48
+ n distinct values are returned (one representative per pair), mirroring
49
+ those other beta=4 ensembles.
50
+ """
51
+
52
+ from __future__ import annotations
53
+
54
+ import numpy as np
55
+
56
+ from .base import MatrixEnsemble
57
+
58
+
59
+ def _dense_wishart(n: int, m: int, rng: np.random.Generator, complex_entries: bool) -> np.ndarray:
60
+ if complex_entries:
61
+ x = (rng.standard_normal((n, m)) + 1j * rng.standard_normal((n, m))) / np.sqrt(2.0)
62
+ else:
63
+ x = rng.standard_normal((n, m))
64
+ return x @ x.conj().T
65
+
66
+
67
+ def _quaternion_gaussian_block(n: int, m: int, rng: np.random.Generator) -> np.ndarray:
68
+ """N x M quaternion matrix, embedded as a 2N x 2M complex matrix
69
+ (same convention as ``physicskit.rmt.ensembles.ginibre.GinSE``)."""
70
+ a = rng.standard_normal((n, m))
71
+ b = rng.standard_normal((n, m))
72
+ c = rng.standard_normal((n, m))
73
+ d = rng.standard_normal((n, m))
74
+ w = np.zeros((2 * n, 2 * m), dtype=complex)
75
+ w[0::2, 0::2] = a + 1j * b
76
+ w[0::2, 1::2] = c + 1j * d
77
+ w[1::2, 0::2] = -c + 1j * d
78
+ w[1::2, 1::2] = a - 1j * b
79
+ return w / np.sqrt(2.0)
80
+
81
+
82
+ def _dense_quaternion_wishart(n: int, m: int, rng: np.random.Generator) -> np.ndarray:
83
+ x = _quaternion_gaussian_block(n, m, rng)
84
+ return x @ x.conj().T
85
+
86
+
87
+ class JacobiBetaEnsemble(MatrixEnsemble):
88
+ """General (beta=1, 2, or 4) Jacobi/MANOVA ensemble.
89
+
90
+ ``n`` is the ensemble/matrix size (per the ``MatrixEnsemble`` base
91
+ class, matching the other ensembles' convention -- for beta=4 this
92
+ is the number of *distinct* eigenvalues, per the module docstring);
93
+ ``m1``, ``m2`` are the two Wishart "sample sizes" (each must be >= n).
94
+ """
95
+
96
+ def __init__(
97
+ self,
98
+ n: int,
99
+ m1: int,
100
+ m2: int,
101
+ beta: int,
102
+ seed: int | np.random.Generator | None = None,
103
+ ) -> None:
104
+ """
105
+ Parameters
106
+ ----------
107
+ n : int
108
+ Number of variables (matrix dimension).
109
+ m1 : int
110
+ Sample size of the first Wishart-type matrix; must be >= n.
111
+ m2 : int
112
+ Sample size of the second Wishart-type matrix; must be >= n.
113
+ beta : int
114
+ Dyson index; must be 1, 2, or 4.
115
+ seed : int, numpy.random.Generator, or None, optional
116
+ """
117
+ if beta not in (1, 2, 4):
118
+ raise ValueError(f"JacobiBetaEnsemble only supports beta in (1, 2, 4), got {beta}")
119
+ if m1 < n or m2 < n:
120
+ raise ValueError(f"m1 (samples={m1}) and m2 (samples={m2}) must both be >= n (variables={n})")
121
+ super().__init__(n, seed=seed)
122
+ self.m1 = m1
123
+ self.m2 = m2
124
+ self.beta: float = beta
125
+
126
+ @property
127
+ def a(self) -> float:
128
+ """Inverse aspect ratio m1/n (Erdos-Farrell/Wachter parametrization)."""
129
+ return self.m1 / self.n
130
+
131
+ @property
132
+ def b(self) -> float:
133
+ """Inverse aspect ratio m2/n."""
134
+ return self.m2 / self.n
135
+
136
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
137
+ if self.beta == 4:
138
+ mat_a = _dense_quaternion_wishart(self.n, self.m1, rng)
139
+ mat_b = _dense_quaternion_wishart(self.n, self.m2, rng)
140
+ else:
141
+ complex_entries = self.beta == 2
142
+ mat_a = _dense_wishart(self.n, self.m1, rng, complex_entries)
143
+ mat_b = _dense_wishart(self.n, self.m2, rng, complex_entries)
144
+ s = mat_a + mat_b
145
+ w, v = np.linalg.eigh(s)
146
+ s_inv_sqrt = (v * (1.0 / np.sqrt(w))) @ v.conj().T
147
+ m = s_inv_sqrt @ mat_a @ s_inv_sqrt
148
+ m = (m + m.conj().T) / 2.0 # symmetrize away numerical asymmetry
149
+ eigenvalues = np.linalg.eigvalsh(m)
150
+ if self.beta == 4:
151
+ # Exact Kramers double degeneracy (see module docstring) --
152
+ # keep one representative per pair, as GSE/CSE/EffGSE do.
153
+ eigenvalues = np.sort(eigenvalues)[0::2]
154
+ return eigenvalues
155
+
156
+ def natural_scale(self) -> float:
157
+ # Eigenvalues already live in the natural [0, 1] range; no rescaling.
158
+ return 1.0
159
+
160
+
161
+ class JOE(JacobiBetaEnsemble):
162
+ """Jacobi Orthogonal Ensemble (beta=1): real double-Wishart ratio."""
163
+
164
+ def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
165
+ super().__init__(n, m1, m2, beta=1, seed=seed)
166
+
167
+
168
+ class JUE(JacobiBetaEnsemble):
169
+ """Jacobi Unitary Ensemble (beta=2): complex double-Wishart ratio."""
170
+
171
+ def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
172
+ super().__init__(n, m1, m2, beta=2, seed=seed)
173
+
174
+
175
+ class JSE(JacobiBetaEnsemble):
176
+ """Jacobi Symplectic Ensemble (beta=4): quaternionic double-Wishart
177
+ ratio. ``n`` is the quaternionic dimension; each sample produces n
178
+ distinct eigenvalues (each an exact Kramers pair internally -- see
179
+ module docstring)."""
180
+
181
+ def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
182
+ super().__init__(n, m1, m2, beta=4, seed=seed)
@@ -0,0 +1,77 @@
1
+ """Poisson ensemble -- the integrable-system null model (Berry-Tabor
2
+ conjecture).
3
+
4
+ Reference
5
+ ---------
6
+ M. V. Berry, M. Tabor, "Level clustering in the regular spectrum",
7
+ Proc. R. Soc. Lond. A 356 (1977) 375 -- the semiclassical argument that
8
+ quantized energy levels of a (generic) classically INTEGRABLE system are,
9
+ locally, statistically indistinguishable from an uncorrelated Poisson
10
+ point process, in sharp contrast to the level REPULSION seen in
11
+ GOE/GUE/GSE for classically CHAOTIC systems (the complementary
12
+ Bohigas-Giannoni-Schmit conjecture, Phys. Rev. Lett. 52 (1984) 1).
13
+
14
+ This is not a matrix ensemble in the sense every other class in this
15
+ package is -- there is no underlying random matrix, Dyson index, or
16
+ symmetry class here. It is the standard baseline/null model that RMT
17
+ level statistics (spacing distribution, ratio statistic, number
18
+ variance) are always contrasted against; see
19
+ ``physicskit.rmt.stats.rigidity.number_variance_poisson``, which until this
20
+ ensemble existed had no matching sampler in this package to validate it
21
+ against empirically (see ``tests/test_poisson.py``).
22
+
23
+ Construction: n levels are generated as the partial sums of n i.i.d.
24
+ Exponential(1) spacings -- i.e. a single realization of a homogeneous
25
+ Poisson point process of UNIT RATE on the positive real line. This is
26
+ deliberately NOT "n i.i.d. Uniform(0, n) points" (a common shortcut):
27
+ that construction only approaches Poisson-process statistics
28
+ asymptotically in the bulk (finite-interval edge effects bias it at
29
+ finite n), whereas partial sums of i.i.d. Exponential(1) spacings give
30
+ EXACTLY unit-rate Poisson-process statistics at every n -- consecutive
31
+ spacings are exactly i.i.d. Exponential(1) by construction, not merely
32
+ on average.
33
+
34
+ Because the process has constant unit rate everywhere (translation
35
+ invariant), it needs no unfolding: unlike GOE/Wishart's compact,
36
+ nontrivially-shaped limiting density (which spacing/rigidity statistics
37
+ must first divide out via a CDF -- see ``physicskit.rmt.stats.unfolding``), the
38
+ levels sampled here already have exactly unit mean spacing throughout --
39
+ the same "no separate unfolding step needed" situation as the circular
40
+ ensembles (``physicskit.rmt.ensembles.circular``), though for the opposite
41
+ reason (uniform density on an unbounded line here, vs. exact rotational
42
+ invariance on a compact circle there).
43
+
44
+ ``beta`` is left ``None`` (inherited from the base class): Poisson
45
+ statistics are not part of Dyson's threefold way at all -- they are the
46
+ uncorrelated baseline every beta-ensemble's level repulsion is
47
+ contrasted against, NOT a beta -> 0 limit of the Gaussian-tailed Wigner-
48
+ surmise family used for GOE/GUE/GSE. That limit is a half-Gaussian
49
+ (Rayleigh-shaped) distribution, not the exponential spacing law of a
50
+ true Poisson process -- worth knowing if you're tempted to reuse
51
+ ``physicskit.rmt.validation.WignerSurmise(beta=0)`` or
52
+ ``physicskit.rmt.validation.RatioDistribution(beta=0)`` here; both would
53
+ silently validate against the wrong theoretical curve. The exact
54
+ Poisson-process laws instead are: spacing PDF exp(-s) (``scipy.stats.expon``);
55
+ and, for the min/max-normalized ratio statistic r in [0, 1] used by
56
+ ``physicskit.rmt.stats.ratios.ratio_statistics``, PDF 2/(1+r)**2, CDF 2r/(1+r)
57
+ -- derived directly here (not taken from a remembered formula) from the
58
+ ratio of two i.i.d. Exponential(1) variables, and checked numerically in
59
+ the test suite.
60
+ """
61
+
62
+ import numpy as np
63
+
64
+ from .base import MatrixEnsemble
65
+
66
+
67
+ class PoissonEnsemble(MatrixEnsemble):
68
+ """n "energy levels" of an integrable system's Berry-Tabor null
69
+ model: a unit-rate homogeneous Poisson point process, realized as
70
+ the partial sums of n i.i.d. Exponential(1) spacings.
71
+ """
72
+
73
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
74
+ return np.cumsum(rng.exponential(scale=1.0, size=self.n))
75
+
76
+ def natural_scale(self) -> float:
77
+ return 1.0
@@ -0,0 +1,139 @@
1
+ """Polynomial ensembles -- squared singular values of a product of
2
+ independent Ginibre matrices; a genuine BIORTHOGONAL ensemble (Borodin's
3
+ general determinantal framework) whenever more than one factor is used.
4
+
5
+ References
6
+ ----------
7
+ A. B. J. Kuijlaars, D. Stivigny, "Singular values of products of random
8
+ matrices and polynomial ensembles", Random Matrices Theory Appl. 3
9
+ (2014) 1450011 -- the "polynomial ensemble" terminology, and the theorem
10
+ that products of Ginibre matrices realize one, with explicit
11
+ Meijer-G-function biorthogonal weight functions.
12
+ A. Borodin, "Biorthogonal ensembles", Nucl. Phys. B 536 (1998) 704 --
13
+ the general determinantal framework: joint density
14
+ prod_{i<j}(x_i-x_j) * det[phi_k(x_j)] * det[psi_k(x_j)], of which the
15
+ classical orthogonal-polynomial ensembles (a single factor, L=1 below)
16
+ are the special case phi_k = psi_k (true orthogonal polynomials).
17
+ E. Strahov, "Differential equations for singular values of products of
18
+ Ginibre random matrices", J. Phys. A 47 (2014) 325203 -- the explicit
19
+ non-orthogonal-polynomial biorthogonal kernel for L >= 2, confirming
20
+ that construction is NOT reducible to a classical orthogonal-polynomial
21
+ ensemble.
22
+ G. Akemann, J. R. Ipsen, M. Kieburg, "Products of rectangular random
23
+ matrices: singular values and progressive scattering", Phys. Rev. E 88
24
+ (2013) 052118.
25
+ K. A. Penson, K. Zyczkowski, "Product of Ginibre matrices: Fuss-Catalan
26
+ and Raney distributions", Phys. Rev. E 83 (2011) 061118 -- the
27
+ Fuss-Catalan limiting law and moment formula used for validation here.
28
+
29
+ Construction
30
+ ------------
31
+ X = X_1 @ X_2 @ ... @ X_L, each X_k an independent n x n Ginibre matrix
32
+ (real, beta=1, or complex, beta=2). This ensemble's eigenvalues are the
33
+ squared singular values of X, i.e. the eigenvalues of X^dagger @ X.
34
+
35
+ L=1 recovers the classical Wishart/Laguerre ensemble EXACTLY (a genuine
36
+ orthogonal-polynomial ensemble, phi_k=psi_k in Borodin's framework) --
37
+ used as a strong validation anchor in the test suite (a direct match
38
+ against the already-validated ``LaguerreBetaEnsemble``/
39
+ ``MarchenkoPastur`` machinery), not a separate approximation.
40
+
41
+ L >= 2 is a GENUINE biorthogonal ensemble: Kuijlaars-Stivigny prove the
42
+ joint eigenvalue density still has the determinantal
43
+ Delta(x) * det[phi_k(x_j)] * det[psi_k(x_j)] form (making it a
44
+ "polynomial ensemble"), but Strahov shows the correlation kernel is
45
+ provably NOT the reproducing kernel of any single family of orthogonal
46
+ polynomials once L >= 2 -- phi_k and psi_k are then genuinely different
47
+ function families (built from Meijer G-functions), the defining feature
48
+ that distinguishes a biorthogonal ensemble from the classical
49
+ orthogonal-polynomial ensembles making up most of the rest of this
50
+ package. ``num_factors`` therefore defaults to 2 here, not 1, so the
51
+ default instance actually exhibits this genuine biorthogonal structure
52
+ rather than silently reducing to the classical case.
53
+
54
+ After the standard n**L rescaling, the limiting eigenvalue distribution
55
+ is the Fuss-Catalan law with parameter L (Penson-Zyczkowski 2011):
56
+ support [0, (L+1)**(L+1) / L**L] and exact moments
57
+
58
+ m_k = binom((L+1)*k, k) / (L*k + 1)
59
+
60
+ (L=1 recovers the Marchenko-Pastur moments at gamma=1, i.e. the Catalan
61
+ numbers, as it must). ``natural_scale`` divides by this n**L factor
62
+ accordingly -- both the scaling exponent and the exact moment formula
63
+ were verified numerically during development, at L=1..4, against Monte
64
+ Carlo before being used here (not assumed from memory) -- see
65
+ ``tests/test_polynomial.py``.
66
+
67
+ Only beta in {1, 2} are supported here (real or complex Ginibre
68
+ factors), matching the precedent set by
69
+ ``physicskit.rmt.ensembles.universality.GeneralWignerEnsemble``: a quaternionic
70
+ (beta=4) product-of-Ginibre construction is a further, separate
71
+ extension not implemented here.
72
+ """
73
+
74
+ from __future__ import annotations
75
+
76
+ from math import comb
77
+
78
+ import numpy as np
79
+
80
+ from .base import MatrixEnsemble
81
+
82
+
83
+ def fuss_catalan_moment(k: int, num_factors: int) -> float:
84
+ """Exact k-th moment of the Fuss-Catalan(L) distribution -- the
85
+ limiting law of a product of L independent square Ginibre matrices'
86
+ squared singular values, after the n**L rescaling.
87
+
88
+ m_k = binom((L+1)*k, k) / (L*k + 1). L=1 recovers the standard
89
+ Marchenko-Pastur moments at gamma=1 (Catalan numbers).
90
+ """
91
+ return comb((num_factors + 1) * k, k) / (num_factors * k + 1)
92
+
93
+
94
+ class PolynomialEnsemble(MatrixEnsemble):
95
+ """Squared singular values of a product of ``num_factors`` independent
96
+ n x n Ginibre matrices -- a polynomial/biorthogonal ensemble (see
97
+ module docstring).
98
+
99
+ Parameters
100
+ ----------
101
+ n : int
102
+ num_factors : int, optional
103
+ Number of independent Ginibre factors L (default 2, the
104
+ smallest case exhibiting genuine biorthogonal, non-orthogonal-
105
+ polynomial structure; L=1 recovers the classical Wishart/
106
+ Laguerre ensemble exactly).
107
+ beta : int, optional
108
+ 1 (real Ginibre factors) or 2 (complex Ginibre factors).
109
+ """
110
+
111
+ def __init__(
112
+ self,
113
+ n: int,
114
+ num_factors: int = 2,
115
+ beta: int = 2,
116
+ seed: int | np.random.Generator | None = None,
117
+ ) -> None:
118
+ if num_factors < 1:
119
+ raise ValueError(f"num_factors must be >= 1, got {num_factors}")
120
+ if beta not in (1, 2):
121
+ raise ValueError(f"beta must be 1 or 2, got {beta}")
122
+ super().__init__(n, seed=seed)
123
+ self.num_factors = num_factors
124
+ self.beta: float = beta
125
+
126
+ def _sample_factor(self, rng: np.random.Generator) -> np.ndarray:
127
+ n = self.n
128
+ if self.beta == 1:
129
+ return rng.standard_normal((n, n))
130
+ return (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
131
+
132
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
133
+ x = self._sample_factor(rng)
134
+ for _ in range(self.num_factors - 1):
135
+ x = x @ self._sample_factor(rng)
136
+ return np.linalg.eigvalsh(x.conj().T @ x)
137
+
138
+ def natural_scale(self) -> float:
139
+ return float(self.n) ** self.num_factors
@@ -0,0 +1,120 @@
1
+ """PT-symmetric (pseudo-Hermitian) random matrix ensembles.
2
+
3
+ References
4
+ ----------
5
+ C. M. Bender, S. Boettcher, "Real Spectra in Non-Hermitian Hamiltonians
6
+ Having PT Symmetry", Phys. Rev. Lett. 80 (1998) 5243 -- the original
7
+ PT-symmetric-quantum-mechanics observation this construction realizes
8
+ as a random matrix model.
9
+ A. Mostafazadeh, "Pseudo-Hermiticity versus PT-symmetry", J. Math. Phys.
10
+ 43 (2002) 205, 2814, 3944 -- the equivalence (for a diagonalizable H
11
+ with real spectrum) between PT-symmetry and pseudo-Hermiticity
12
+ P H P^{-1} = H^dagger for some Hermitian, invertible P, which is the
13
+ concrete condition realized here (P chosen as a fixed, real, diagonal
14
+ +-1 "signature" matrix, so P = P^dagger = P^{-1}).
15
+
16
+ Construction: partition the n = p + q dimensions into a "+1" block
17
+ (size p) and a "-1" block (size q), P = diag(I_p, -I_q). Writing H in
18
+ the matching 2x2 block form,
19
+
20
+ H = [[A, B], [C, D]]
21
+
22
+ the pseudo-Hermiticity condition P H P = H^dagger forces A = A^dagger
23
+ (p x p Hermitian), D = D^dagger (q x q Hermitian), and C = -B^dagger
24
+ (NOT the +B^dagger a genuinely Hermitian H would need) -- verified
25
+ numerically here (``max|PHP - H^dagger|`` to machine precision) before
26
+ being trusted. A, D are drawn as independent GOE/GUE-type Hermitian
27
+ blocks; B is an independent p x q Ginibre-type block scaled by a
28
+ non-Hermiticity/coupling parameter ``g``.
29
+
30
+ Physics (verified numerically during development, see
31
+ ``tests/test_pt_symmetric.py``): at g=0, H is block-diagonal (A
32
+ directly summed with D), so its spectrum is trivially real (each block
33
+ is Hermitian) -- the fully "unbroken" PT-symmetric phase. Since H is
34
+ similar to H^dagger (via the invertible P), its eigenvalue set is
35
+ always closed under complex conjugation: every eigenvalue is either
36
+ real or part of an exact complex-conjugate pair -- never an isolated
37
+ complex value. As g increases from 0, an increasing fraction of
38
+ eigenvalues collide in real-conjugate pairs and move off the real axis
39
+ as complex-conjugate pairs (each such collision is an "exceptional
40
+ point": both the eigenvalues AND their eigenvectors coalesce there,
41
+ unlike an ordinary level crossing) -- the celebrated PT-symmetry-
42
+ breaking transition. See ``physicskit.rmt.stats.pt_symmetric`` for the fraction-
43
+ of-real-eigenvalues statistic that tracks this transition.
44
+ """
45
+
46
+ from __future__ import annotations
47
+
48
+ import numpy as np
49
+
50
+ from .base import MatrixEnsemble
51
+
52
+
53
+ def _dense_hermitian_block(n: int, rng: np.random.Generator, complex_entries: bool) -> np.ndarray:
54
+ if complex_entries:
55
+ x = (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
56
+ return (x + x.conj().T) / 2.0
57
+ x = rng.standard_normal((n, n))
58
+ return (x + x.T) / np.sqrt(2.0)
59
+
60
+
61
+ class PTSymmetricEnsemble(MatrixEnsemble):
62
+ """Pseudo-Hermitian (PT-symmetric-realizing) random matrix ensemble
63
+ (see module docstring). ``n`` (inherited from ``MatrixEnsemble``) is
64
+ the total dimension p + q.
65
+
66
+ Parameters
67
+ ----------
68
+ p : int
69
+ Size of the "+1"-signature block.
70
+ q : int
71
+ Size of the "-1"-signature block.
72
+ g : float
73
+ Non-Hermiticity / coupling strength scaling the off-diagonal
74
+ block B (g=0: block-diagonal, trivially real spectrum; larger
75
+ g: more eigenvalues driven into complex-conjugate pairs).
76
+ beta : int, optional
77
+ 1 (real blocks) or 2 (complex Hermitian blocks, default).
78
+ seed : int, numpy.random.Generator, or None, optional
79
+ """
80
+
81
+ def __init__(
82
+ self,
83
+ p: int,
84
+ q: int,
85
+ g: float,
86
+ beta: int = 2,
87
+ seed: int | np.random.Generator | None = None,
88
+ ) -> None:
89
+ if beta not in (1, 2):
90
+ raise ValueError(f"PTSymmetricEnsemble only supports beta in (1, 2), got {beta}")
91
+ if g < 0:
92
+ raise ValueError(f"g (coupling strength) must be >= 0, got {g}")
93
+ super().__init__(p + q, seed=seed)
94
+ self.p = p
95
+ self.q = q
96
+ self.g = float(g)
97
+ self.beta: float = beta
98
+
99
+ def _sample_matrix(self, rng: np.random.Generator) -> np.ndarray:
100
+ complex_entries = self.beta == 2
101
+ a = _dense_hermitian_block(self.p, rng, complex_entries)
102
+ d = _dense_hermitian_block(self.q, rng, complex_entries)
103
+ if complex_entries:
104
+ b = self.g * (rng.standard_normal((self.p, self.q)) + 1j * rng.standard_normal((self.p, self.q))) / np.sqrt(2.0)
105
+ else:
106
+ b = self.g * rng.standard_normal((self.p, self.q))
107
+ c = -b.conj().T
108
+ top = np.hstack([a, b])
109
+ bottom = np.hstack([c, d])
110
+ return np.vstack([top, bottom])
111
+
112
+ def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
113
+ return np.linalg.eigvals(self._sample_matrix(rng)).astype(complex)
114
+
115
+ def natural_scale(self) -> float:
116
+ # No single universal rescaling: unlike the Hermitian ensembles,
117
+ # this ensemble's spectral shape genuinely changes character
118
+ # (real -> partially complex) as g varies, so there is no fixed
119
+ # limiting law to normalize against.
120
+ return 1.0