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,265 @@
1
+ """Statistics for the PT-symmetry-breaking transition (see
2
+ ``physicskit.rmt.ensembles.pt_symmetric``): the fraction of real eigenvalues as
3
+ a function of the non-Hermiticity/coupling parameter g -- the
4
+ transition curve itself is the benchmark here, not a fixed limiting
5
+ law, since the ensemble's spectral character genuinely changes (real ->
6
+ partially complex) as g varies.
7
+
8
+ Also implemented: exceptional-point detection. An exceptional point is
9
+ where two eigenvalues AND their eigenvectors coalesce as a control
10
+ parameter varies -- unlike an ordinary level crossing (eigenvalues
11
+ cross, eigenvectors stay linearly independent), ``find_exceptional_points``
12
+ locates these by sweeping g for a single FIXED random realization (same
13
+ underlying blocks, only g rescaling the coupling block) and finding
14
+ where the real-eigenvalue count drops. Verified numerically during
15
+ development that these are genuine eigenvector coalescences, not mere
16
+ crossings: the right-eigenvector matrix's condition number grows sharply
17
+ approaching each detected g (2 -> 6 -> 38 within 0.002 of one such point
18
+ in a test case), the signature of the matrix becoming nearly
19
+ non-diagonalizable there -- see ``eigenvector_condition_number`` and
20
+ ``tests/test_pt_symmetric.py``.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import numpy as np
26
+
27
+ from ..ensembles.pt_symmetric import PTSymmetricEnsemble
28
+
29
+
30
+ def real_eigenvalue_fraction(eigenvalues: np.ndarray, tol: float = 1e-6) -> np.ndarray:
31
+ """Fraction of (near-)real eigenvalues per sample.
32
+
33
+ Parameters
34
+ ----------
35
+ eigenvalues : numpy.ndarray, shape (n_samples, n), complex
36
+ tol : float, optional
37
+ An eigenvalue counts as real if ``abs(Im(lambda))`` is below
38
+ this.
39
+
40
+ Returns
41
+ -------
42
+ numpy.ndarray, shape (n_samples,)
43
+ Fraction (in [0, 1]) of real eigenvalues in each sample.
44
+ """
45
+ eigenvalues = np.asarray(eigenvalues)
46
+ return np.mean(np.abs(eigenvalues.imag) < tol, axis=-1)
47
+
48
+
49
+ def semi_poisson_pdf(s: np.ndarray) -> np.ndarray:
50
+ """The semi-Poisson intermediate-statistics spacing density:
51
+ P(s) = 4*s*exp(-2*s). Normalized (integral P ds = 1) and unit mean
52
+ spacing (integral s*P ds = 1) -- checked directly here (both
53
+ integrals evaluate to exactly 1 in closed form) rather than assumed.
54
+
55
+ Characterizes level spacing at various "critical"/intermediate
56
+ universality classes between Poisson (uncorrelated) and Wigner-Dyson
57
+ (GOE-type): linear level repulsion at small s (like GOE, unlike
58
+ Poisson's flat P(0)=1), but exponential (not Gaussian) large-s decay
59
+ (like Poisson, unlike GOE's exp(-s^2)). Verified numerically here to
60
+ be a substantially better fit than either pure Poisson or pure GOE
61
+ to the surviving REAL eigenvalues' spacing distribution of
62
+ ``PTSymmetricEnsemble`` at small-to-moderate coupling g (KS
63
+ statistic roughly half that of the alternatives at g up to ~0.6) --
64
+ see ``tests/test_pt_symmetric.py``.
65
+
66
+ Parameters
67
+ ----------
68
+ s : numpy.ndarray
69
+
70
+ Returns
71
+ -------
72
+ numpy.ndarray
73
+ """
74
+ s = np.asarray(s, dtype=float)
75
+ return 4.0 * s * np.exp(-2.0 * s)
76
+
77
+
78
+ def semi_poisson_cdf(s: np.ndarray) -> np.ndarray:
79
+ """CDF of the semi-Poisson spacing density: F(s) = 1 - (2s+1)*exp(-2s).
80
+
81
+ Parameters
82
+ ----------
83
+ s : numpy.ndarray
84
+
85
+ Returns
86
+ -------
87
+ numpy.ndarray
88
+ """
89
+ s = np.asarray(s, dtype=float)
90
+ return 1.0 - (2.0 * s + 1.0) * np.exp(-2.0 * s)
91
+
92
+
93
+ def real_axis_spacings(eigenvalues: np.ndarray, tol: float = 1e-6) -> np.ndarray:
94
+ """Nearest-neighbor spacings of the (near-)real eigenvalues in each
95
+ sample, rescaled to unit mean spacing per sample and pooled.
96
+
97
+ No unfolding CDF is used (unlike
98
+ ``physicskit.rmt.stats.spacing.nearest_neighbor_spacings``): the real-
99
+ eigenvalue density of a PT-symmetric ensemble near its
100
+ symmetry-breaking transition has no known closed form, so this uses
101
+ the simple, standard alternative of rescaling each sample's raw
102
+ spacings by their own empirical mean -- adequate for a KS/shape
103
+ comparison against ``semi_poisson_cdf``, though not as precise as
104
+ unfolding against a known exact density.
105
+
106
+ Parameters
107
+ ----------
108
+ eigenvalues : numpy.ndarray, shape (n_samples, n), complex
109
+ tol : float, optional
110
+ Passed to the real/complex classification (see
111
+ ``real_eigenvalue_fraction``).
112
+
113
+ Returns
114
+ -------
115
+ numpy.ndarray
116
+ Pooled, mean-rescaled nearest-neighbor spacings of the real
117
+ eigenvalues (samples with fewer than 3 real eigenvalues are
118
+ skipped, since a mean spacing needs at least 2 spacings).
119
+ """
120
+ pooled = []
121
+ for row in eigenvalues:
122
+ real_values = np.sort(row.real[np.abs(row.imag) < tol])
123
+ if len(real_values) < 3:
124
+ continue
125
+ spacings = np.diff(real_values)
126
+ spacings = spacings[spacings > 1e-12]
127
+ if len(spacings) < 2:
128
+ continue
129
+ pooled.append(spacings / spacings.mean())
130
+ return np.concatenate(pooled)
131
+
132
+
133
+ def _fixed_realization_matrix(p: int, q: int, beta: int, seed: int, g: float) -> np.ndarray:
134
+ """The PT-symmetric matrix for a SINGLE fixed random realization
135
+ (blocks A, D, and the unscaled coupling direction determined
136
+ entirely by ``seed``), at coupling strength ``g``. Since
137
+ ``PTSymmetricEnsemble._sample_matrix`` draws A, D, then the coupling
138
+ block (multiplied by g only at the very end) in a fixed order from a
139
+ generator seeded identically each call, reconstructing the ensemble
140
+ with a different g but the same seed reproduces the same A, D and
141
+ unscaled coupling direction -- verified during development (see
142
+ module docstring) -- making it possible to sweep g continuously for
143
+ one realization rather than redrawing new randomness at every g.
144
+ """
145
+ ensemble = PTSymmetricEnsemble(p=p, q=q, g=g, beta=beta, seed=seed)
146
+ return ensemble._sample_matrix(np.random.default_rng(seed))
147
+
148
+
149
+ def find_exceptional_points(
150
+ p: int,
151
+ q: int,
152
+ beta: int,
153
+ seed: int,
154
+ g_max: float = 5.0,
155
+ n_grid: int = 4000,
156
+ tol: float = 1e-6,
157
+ ) -> np.ndarray:
158
+ """Locate exceptional points for a single fixed PT-symmetric
159
+ realization by sweeping the coupling g in ``[0, g_max]`` and finding
160
+ where the real-eigenvalue count decreases (a pair of real
161
+ eigenvalues has merged and split into a complex-conjugate pair).
162
+
163
+ Parameters
164
+ ----------
165
+ p, q : int
166
+ Signature block sizes (see ``PTSymmetricEnsemble``).
167
+ beta : int
168
+ 1 or 2.
169
+ seed : int
170
+ Fixes the single realization swept over g.
171
+ g_max : float, optional
172
+ Upper end of the g-sweep.
173
+ n_grid : int, optional
174
+ Number of g-grid points.
175
+ tol : float, optional
176
+ Passed to ``real_eigenvalue_fraction``.
177
+
178
+ Returns
179
+ -------
180
+ numpy.ndarray
181
+ g-values (grid resolution) at which a real-eigenvalue-count drop
182
+ was detected -- approximate exceptional-point locations for this
183
+ realization.
184
+ """
185
+ g_grid = np.linspace(0.0, g_max, n_grid)
186
+ real_counts = np.empty(n_grid)
187
+ for i, g in enumerate(g_grid):
188
+ eigenvalues = np.linalg.eigvals(_fixed_realization_matrix(p, q, beta, seed, g))
189
+ real_counts[i] = np.sum(np.abs(eigenvalues.imag) < tol)
190
+ drops = np.where(np.diff(real_counts) < 0)[0]
191
+ return g_grid[drops]
192
+
193
+
194
+ def mean_exceptional_point_count(
195
+ p: int,
196
+ q: int,
197
+ beta: int,
198
+ g_max: float,
199
+ n_realizations: int = 30,
200
+ seed: int | np.random.Generator | None = None,
201
+ n_grid: int = 2000,
202
+ tol: float = 1e-6,
203
+ ) -> tuple[float, float]:
204
+ """Ensemble-level exceptional-point statistic: the mean number of
205
+ exceptional points found in ``[0, g_max]`` (see
206
+ ``find_exceptional_points``), averaged over ``n_realizations``
207
+ independent random realizations.
208
+
209
+ Verified numerically during development to grow roughly linearly
210
+ with ``min(p, q)`` at fixed g_max (e.g. p=q=4, 8, 12 gave mean counts
211
+ of about 4.2, 8.7, 13.1 over g in [0, 2] with beta=2) -- consistent
212
+ with the expectation that roughly min(p, q) real-eigenvalue pairs
213
+ must eventually transition as g grows, though this specific scaling
214
+ was observed rather than derived from a closed-form prediction.
215
+
216
+ Parameters
217
+ ----------
218
+ p, q : int
219
+ beta : int
220
+ g_max : float
221
+ n_realizations : int, optional
222
+ Independent random realizations (distinct seeds) to average
223
+ over.
224
+ seed : int, numpy.random.Generator, or None, optional
225
+ Seeds the sequence of per-realization seeds.
226
+ n_grid, tol : see ``find_exceptional_points``.
227
+
228
+ Returns
229
+ -------
230
+ mean_count : float
231
+ standard_error : float
232
+ """
233
+ rng = np.random.default_rng(seed)
234
+ raw_counts = []
235
+ for _ in range(n_realizations):
236
+ realization_seed = int(rng.integers(1 << 31))
237
+ eps = find_exceptional_points(p, q, beta, realization_seed, g_max, n_grid, tol)
238
+ raw_counts.append(len(eps))
239
+ counts = np.array(raw_counts, dtype=float)
240
+ return float(counts.mean()), float(counts.std(ddof=1) / np.sqrt(n_realizations))
241
+
242
+
243
+ def eigenvector_condition_number(p: int, q: int, beta: int, seed: int, g: float) -> float:
244
+ """Condition number of the right-eigenvector matrix of the fixed-
245
+ realization PT-symmetric matrix (see ``find_exceptional_points``) at
246
+ coupling ``g``. Diverges approaching a genuine exceptional point
247
+ (eigenvectors becoming parallel, the matrix approaching a Jordan
248
+ block there) -- unlike an ordinary level crossing, where eigenvectors
249
+ stay well-conditioned. The confirmatory structural check that
250
+ distinguishes an exceptional point from an incidental crossing.
251
+
252
+ Parameters
253
+ ----------
254
+ p, q : int
255
+ beta : int
256
+ seed : int
257
+ g : float
258
+
259
+ Returns
260
+ -------
261
+ float
262
+ """
263
+ h = _fixed_realization_matrix(p, q, beta, seed, g)
264
+ _, eigenvectors = np.linalg.eig(h)
265
+ return float(np.linalg.cond(eigenvectors))
@@ -0,0 +1,110 @@
1
+ """Consecutive-spacing ratio statistic (Oganesyan & Huse, Phys. Rev. B 75,
2
+ 155111, 2007) and its closed-form surmise (Atas, Bogomolny, Giraud & Roux,
3
+ Phys. Rev. Lett. 110, 084101, 2013).
4
+
5
+ The ratio statistic::
6
+
7
+ r_n = min(s_n, s_{n-1}) / max(s_n, s_{n-1}) in [0, 1]
8
+
9
+ where s_n = x_{n+1} - x_n are consecutive *raw* spacings, is invariant
10
+ under any smooth local rescaling of the eigenvalues -- unlike the spacing
11
+ distribution P(s), it requires no unfolding. That makes it a genuinely
12
+ independent validation of the same underlying level-repulsion physics: if
13
+ both the (unfolding-dependent) spacing distribution and the
14
+ (unfolding-free) ratio distribution agree with their respective surmises,
15
+ unfolding artifacts can be ruled out as the source of agreement.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import numpy as np
21
+ from scipy.integrate import cumulative_trapezoid
22
+ from scipy.interpolate import interp1d
23
+
24
+ from ..spectrum import Spectrum
25
+
26
+
27
+ def ratio_statistics(spectrum: Spectrum, edge_trim: float = 0.1) -> np.ndarray:
28
+ """Pooled ratio statistics from every sample in a Spectrum, computed
29
+ directly on raw eigenvalues (rescaling is irrelevant to a ratio, but
30
+ ``spectrum.rescaled`` is used for consistency with the rest of the
31
+ package).
32
+
33
+ Parameters
34
+ ----------
35
+ spectrum : physicskit.rmt.spectrum.Spectrum
36
+ edge_trim : float
37
+ Fraction of eigenvalues discarded from each edge before taking
38
+ spacings, for the same reason as in ``nearest_neighbor_spacings``.
39
+ No unfolding is involved, so trim order doesn't matter here.
40
+
41
+ Returns
42
+ -------
43
+ numpy.ndarray
44
+ Pooled ratio statistics, each in [0, 1].
45
+ """
46
+ all_ratios = []
47
+ for row in spectrum.rescaled:
48
+ n = len(row)
49
+ lo = int(edge_trim * n)
50
+ hi = n - lo
51
+ if hi - lo < 3:
52
+ continue
53
+ s = np.diff(row[lo:hi])
54
+ r = np.minimum(s[:-1], s[1:]) / np.maximum(s[:-1], s[1:])
55
+ all_ratios.append(r)
56
+ return np.concatenate(all_ratios)
57
+
58
+
59
+ def _ratio_surmise_unnormalized_pdf(r: np.ndarray, beta: float) -> np.ndarray:
60
+ r = np.asarray(r, dtype=float)
61
+ return (r + r**2) ** beta / (1.0 + r + r**2) ** (1.0 + 1.5 * beta)
62
+
63
+
64
+ class RatioSurmise:
65
+ """The Atas et al. (2013) surmise for the ratio statistic, restricted
66
+ to r in [0, 1] (the min/max-normalized convention used by
67
+ ``ratio_statistics`` above).
68
+
69
+ The normalizing constant and CDF have no simple closed form, so both
70
+ are built once per beta via a fine-grid cumulative trapezoidal
71
+ integration and cached as interpolators -- this is far cheaper than
72
+ calling ``scipy.integrate.quad`` per evaluation point, which matters
73
+ since a KS test evaluates the CDF at every data point.
74
+ """
75
+
76
+ def __init__(self, beta: float, grid_size: int = 4000) -> None:
77
+ """
78
+ Note on accuracy: this surmise is exact for a reduced (3-level)
79
+ model, not for the true N-level ensemble -- analogous to how the
80
+ classical Wigner surmise is exact for a 2x2 matrix but only an
81
+ excellent approximation at general N. Its own exact mean at
82
+ beta=1 is 4 - 2*sqrt(3) ~= 0.5359 (verified numerically against
83
+ this implementation), which is measurably different from the
84
+ numerically-exact full-GOE mean ratio (~0.5307) reported
85
+ elsewhere in the literature -- both numbers are correct, they
86
+ just answer different questions (surmise vs. true ensemble).
87
+ """
88
+ self.beta = beta
89
+ grid = np.linspace(0.0, 1.0, grid_size)
90
+ pdf_unnorm = _ratio_surmise_unnormalized_pdf(grid, beta)
91
+ cdf_unnorm = cumulative_trapezoid(pdf_unnorm, grid, initial=0.0)
92
+ self._normalization = cdf_unnorm[-1]
93
+ cdf = cdf_unnorm / self._normalization
94
+ self._cdf_interp = interp1d(grid, cdf, bounds_error=False, fill_value=(0.0, 1.0))
95
+ # For sampling: invert the CDF via interpolation the other way.
96
+ # cdf is monotonically increasing, so this is well-defined.
97
+ self._inv_cdf_interp = interp1d(cdf, grid, bounds_error=False, fill_value=(0.0, 1.0))
98
+ self._pdf_grid = grid
99
+ self._pdf_unnorm = pdf_unnorm
100
+
101
+ def pdf(self, r: np.ndarray) -> np.ndarray:
102
+ r = np.asarray(r, dtype=float)
103
+ return np.interp(np.clip(r, 0.0, 1.0), self._pdf_grid, self._pdf_unnorm) / self._normalization
104
+
105
+ def cdf(self, r: np.ndarray) -> np.ndarray:
106
+ return self._cdf_interp(r)
107
+
108
+ def rvs(self, size: int | tuple[int, ...], rng: np.random.Generator) -> np.ndarray:
109
+ u = rng.uniform(0.0, 1.0, size=size)
110
+ return self._inv_cdf_interp(u)
@@ -0,0 +1,134 @@
1
+ """The real-Ginibre real-eigenvalue-count anomaly: a real Ginibre matrix
2
+ (GinOE) has a mix of purely real eigenvalues and complex-conjugate
3
+ pairs, with the expected number of real eigenvalues growing like
4
+ sqrt(n) -- a celebrated result with no Hermitian-ensemble analogue (see
5
+ ``physicskit.rmt.ensembles.ginibre`` module docstring).
6
+
7
+ Reference: A. Edelman, E. Kostlan, S. Shub, "How many eigenvalues of a
8
+ random matrix are real?", J. Amer. Math. Soc. 7 (1994) 247.
9
+
10
+ Precision note: Edelman-Kostlan-Shub's finite-n result is an exact
11
+ closed form built from the real-Ginibre skew-orthogonal Hermite-
12
+ polynomial kernel -- machinery not otherwise needed anywhere else in
13
+ this package, and which this implementation does not reproduce. What IS
14
+ implemented and verified here is the large-n asymptotic expansion
15
+
16
+ E[N_real](n) ~ sqrt(2*n/pi) + 1/2 + O(1/sqrt(n))
17
+
18
+ The leading sqrt(2n/pi) term is the well-known leading-order asymptotic
19
+ already noted in ``physicskit.rmt.ensembles.ginibre``; the +1/2 correction was
20
+ fit against, and confirmed by, direct Monte Carlo real-eigenvalue
21
+ counts on ``GinOE`` at n = 2, 4, ..., 128 during development (residual
22
+ E[N_real] - sqrt(2n/pi) plateaus near 0.5, consistent with this
23
+ correction, rather than assumed from memory) -- see
24
+ ``tests/test_real_ginibre.py``. This is therefore an asymptotic
25
+ formula, not the exact EKS closed form, and is documented as such:
26
+ expect several-percent-level disagreement at small n (n well below 30),
27
+ narrowing as n grows.
28
+ """
29
+
30
+ import numpy as np
31
+
32
+
33
+ def real_eigenvalue_count_empirical(eigenvalues: np.ndarray, tol: float = 1e-8) -> np.ndarray:
34
+ """Count (near-)real eigenvalues per sample.
35
+
36
+ Parameters
37
+ ----------
38
+ eigenvalues : numpy.ndarray, shape (n_samples, n), complex
39
+ Raw complex eigenvalues, e.g. ``GinOE`` ``Spectrum.eigenvalues``.
40
+ tol : float, optional
41
+ An eigenvalue counts as real if ``abs(Im(lambda))`` is below
42
+ this (accounts for floating-point residue from
43
+ ``numpy.linalg.eigvals`` on an exactly-real matrix).
44
+
45
+ Returns
46
+ -------
47
+ numpy.ndarray, shape (n_samples,)
48
+ Integer count of real eigenvalues in each sample.
49
+ """
50
+ eigenvalues = np.asarray(eigenvalues)
51
+ return np.sum(np.abs(eigenvalues.imag) < tol, axis=-1)
52
+
53
+
54
+ def real_eigenvalue_count_asymptotic(n: int) -> float:
55
+ """Large-n asymptotic expected number of real eigenvalues of an
56
+ n x n real Ginibre (GinOE) matrix: sqrt(2*n/pi) + 1/2.
57
+
58
+ See module docstring for precision caveats (asymptotic, not the
59
+ exact Edelman-Kostlan-Shub 1994 finite-n closed form).
60
+
61
+ Parameters
62
+ ----------
63
+ n : int
64
+
65
+ Returns
66
+ -------
67
+ float
68
+ """
69
+ return np.sqrt(2.0 * n / np.pi) + 0.5
70
+
71
+
72
+ def real_eigenvalue_density_empirical(eigenvalues: np.ndarray, n: int, bins: int = 60, x_max: float = 1.2) -> tuple[np.ndarray, np.ndarray]:
73
+ """Empirical density of real eigenvalue positions (rescaled by
74
+ ``sqrt(n)``, matching ``GinOE.natural_scale``): expected number of
75
+ real eigenvalues per unit x per sample, histogrammed over the
76
+ (near-)real eigenvalues pooled across all samples.
77
+
78
+ Parameters
79
+ ----------
80
+ eigenvalues : numpy.ndarray, shape (n_samples, n), complex
81
+ Raw (unrescaled) ``GinOE`` eigenvalues.
82
+ n : int
83
+ Matrix dimension (for the sqrt(n) rescaling).
84
+ bins : int, optional
85
+ x_max : float, optional
86
+ Histogram range is ``[-x_max, x_max]``.
87
+
88
+ Returns
89
+ -------
90
+ centers, density : numpy.ndarray
91
+ """
92
+ eigenvalues = np.asarray(eigenvalues)
93
+ n_samples = eigenvalues.shape[0]
94
+ real_values = eigenvalues.real[np.abs(eigenvalues.imag) < 1e-8] / np.sqrt(n)
95
+ edges = np.linspace(-x_max, x_max, bins + 1)
96
+ counts, _ = np.histogram(real_values, bins=edges)
97
+ bin_width = edges[1] - edges[0]
98
+ centers = 0.5 * (edges[:-1] + edges[1:])
99
+ density = counts / (n_samples * bin_width)
100
+ return centers, density
101
+
102
+
103
+ def real_eigenvalue_density_asymptotic(x: np.ndarray, n: int) -> np.ndarray:
104
+ """Large-n asymptotic expected density of real eigenvalue positions
105
+ (rescaled by sqrt(n)): uniform on (-1, 1) at height
106
+ ``real_eigenvalue_count_asymptotic(n) / 2`` (i.e. the total expected
107
+ real-eigenvalue count spread evenly over the rescaled real-axis
108
+ support), zero outside.
109
+
110
+ Verified numerically during development (not assumed): pooled,
111
+ rescaled real eigenvalues from direct Monte Carlo GinOE samples show
112
+ a flat bulk histogram (no visible x-dependence within statistical
113
+ noise) with a clean cutoff at |x|=1 matching the circular law's disk
114
+ radius, rather than any elevated edge density or bulk curvature --
115
+ see ``tests/test_real_ginibre.py``. Like
116
+ ``real_eigenvalue_count_asymptotic``, this is a verified large-n
117
+ asymptotic description, not the exact finite-n
118
+ Edelman-Kostlan-Shub density (which has additional fine structure,
119
+ e.g. a smooth rather than sharp edge, not captured here).
120
+
121
+ Parameters
122
+ ----------
123
+ x : numpy.ndarray
124
+ n : int
125
+
126
+ Returns
127
+ -------
128
+ numpy.ndarray
129
+ """
130
+ x = np.asarray(x, dtype=float)
131
+ height = real_eigenvalue_count_asymptotic(n) / 2.0
132
+ out = np.zeros_like(x)
133
+ out[np.abs(x) <= 1.0] = height
134
+ return out