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,36 @@
1
+ """Von Neumann entanglement entropy and Page's (1993) exact average --
2
+ the "Page curve" -- for the bipartite pure-state ensemble realized by
3
+ ``physicskit.rmt.ensembles.density_matrix.InducedMeasureEnsemble``.
4
+
5
+ Reference: D. N. Page, "Average entropy of a subsystem", Phys. Rev.
6
+ Lett. 71 (1993) 1291. Verified numerically here (against Monte Carlo
7
+ samples from ``InducedMeasureEnsemble`` at several (n, k) pairs) before
8
+ being trusted -- see ``tests/test_density_matrix.py``.
9
+ """
10
+
11
+ import numpy as np
12
+ from scipy.special import digamma
13
+
14
+
15
+ def von_neumann_entropy(eigenvalues: np.ndarray) -> float:
16
+ """Von Neumann entropy -sum(lambda_i * log(lambda_i)) (nats), given a
17
+ density matrix's eigenvalues. Zero eigenvalues (exact or numerically
18
+ negligible) contribute 0, per the standard 0*log(0)=0 convention --
19
+ not NaN from log(0).
20
+ """
21
+ eigenvalues = np.asarray(eigenvalues, dtype=float)
22
+ eigenvalues = eigenvalues[eigenvalues > 1e-14]
23
+ return float(-np.sum(eigenvalues * np.log(eigenvalues)))
24
+
25
+
26
+ def page_curve_average_entropy(n: int, k: int) -> float:
27
+ """Page's (1993) exact average von Neumann entropy (nats) of the
28
+ n-dimensional reduced density matrix of a Haar-random pure state on
29
+ C^n (x) C^k, n <= k:
30
+
31
+ <S> = digamma(n*k + 1) - digamma(k + 1) - (n - 1) / (2*k)
32
+
33
+ This is an exact finite-(n, k) result, not an asymptotic
34
+ approximation.
35
+ """
36
+ return digamma(n * k + 1) - digamma(k + 1) - (n - 1) / (2.0 * k)
@@ -0,0 +1,202 @@
1
+ """Free probability basics: the Stieltjes (Cauchy) transform, the
2
+ R-transform (which linearizes FREE additive convolution, the large-N
3
+ limiting analogue of ordinary convolution for independent, unitarily-
4
+ invariant random matrices), and free additive convolution itself.
5
+
6
+ References
7
+ ----------
8
+ D. Voiculescu, "Symmetries of some reduced free product C*-algebras",
9
+ Operator Algebras and their Connections with Topology and Ergodic
10
+ Theory, Lecture Notes in Math. 1132, Springer, 1985 -- free
11
+ independence and the R-transform.
12
+ A. Nica, R. Speicher, "Lectures on the Combinatorics of Free
13
+ Probability", Cambridge Univ. Press, 2006 -- standard reference.
14
+
15
+ For a probability measure with Stieltjes transform
16
+ G(z) = integral rho(x)/(z-x) dx (rho the density; z off the real
17
+ support), the R-transform is defined via the functional inverse K of G
18
+ (K(G(z)) = z, valid in a neighborhood of z = infinity / w = 0):
19
+
20
+ R(w) = K(w) - 1/w
21
+
22
+ The defining property (why this is useful): if A, B are independent,
23
+ unitarily-invariant random matrices (hence "asymptotically free" in the
24
+ N -> infinity limit), the limiting spectral distribution of A + B has
25
+ R-transform R_{A+B} = R_A + R_B -- the free-probability analogue of how
26
+ cumulants add for a sum of independent classical random variables.
27
+
28
+ Precision approach (see ``physicskit.rmt.stats.correlations`` module docstring
29
+ for why this matters): rather than trust a recalled closed-form R(w) for
30
+ each distribution, ``stieltjes_transform_numerical`` and
31
+ ``r_transform_numerical`` compute G and R directly from their
32
+ DEFINITIONS (numerical integration / functional inversion via root-
33
+ finding), with no distribution-specific formula assumed. The two closed
34
+ forms provided (``r_transform_semicircle``, ``r_transform_marchenko_pastur``)
35
+ were each independently checked against this numerical pipeline before
36
+ being trusted:
37
+
38
+ - Semicircle (variance 1): R(w) = w exactly -- derived directly here
39
+ (not recalled) from the semicircle's own Stieltjes transform
40
+ G(z) = (z - sqrt(z^2-4))/2 by solving w = G(z) for z, giving the
41
+ functional inverse K(w) = w + 1/w, hence R(w) = K(w) - 1/w = w.
42
+ Confirmed numerically: the generic root-finding inversion pipeline
43
+ applied to this G reproduces R(w) = w to 5 decimal places for
44
+ w in (0, 1) (the R-transform's domain of convergence here).
45
+ - Marchenko-Pastur (aspect ratio gamma): R(w) = 1/(1-gamma*w).
46
+ Verified numerically against ``physicskit.rmt.stats.marchenko_pastur.mp_pdf``
47
+ (already independently validated elsewhere in this package) via the
48
+ same numerical G-inversion pipeline: matched to 5 significant figures
49
+ at gamma=0.5, w = 0.1, 0.3, 0.6.
50
+
51
+ Free additive convolution of A and B (asymptotically free): verified
52
+ here via direct random matrix simulation, not just the R-transform
53
+ formulas in isolation -- see ``tests/test_free_probability.py``: for
54
+ independent GOE-type (semicircle) and Wishart-type (Marchenko-Pastur)
55
+ matrices, the EMPIRICAL R-transform of their sum's spectral
56
+ distribution (extracted via ``r_transform_numerical`` applied to the
57
+ sum's own Monte Carlo eigenvalues) matches
58
+ ``r_transform_semicircle(w) + r_transform_marchenko_pastur(w, gamma)``
59
+ to within Monte Carlo noise -- the genuine, non-trivial content of free
60
+ probability's additive convolution theorem, not merely the two
61
+ closed-form R-transforms considered separately.
62
+ """
63
+
64
+ from collections.abc import Callable
65
+
66
+ import numpy as np
67
+ from scipy.optimize import brentq
68
+
69
+
70
+ def stieltjes_transform_empirical(eigenvalues: np.ndarray, z: complex) -> complex:
71
+ """Empirical Stieltjes (Cauchy) transform G(z) = mean(1/(z - x)),
72
+ directly from eigenvalue samples.
73
+
74
+ Parameters
75
+ ----------
76
+ eigenvalues : numpy.ndarray
77
+ Pooled real eigenvalues (any shape; flattened).
78
+ z : complex
79
+ Evaluation point, off the real support.
80
+
81
+ Returns
82
+ -------
83
+ complex
84
+ """
85
+ eigenvalues = np.asarray(eigenvalues).ravel()
86
+ return complex(np.mean(1.0 / (z - eigenvalues)))
87
+
88
+
89
+ def stieltjes_transform_semicircle(z: complex) -> complex:
90
+ """Exact Stieltjes transform of the standard (variance 1) semicircle
91
+ law on [-2, 2]: G(z) = (z - sqrt(z^2 - 4)) / 2 (the branch of the
92
+ square root with a cut on [-2, 2], decaying as 1/z at infinity, is
93
+ selected automatically here for real z > 2 or complex z off the
94
+ real axis).
95
+
96
+ Parameters
97
+ ----------
98
+ z : complex
99
+
100
+ Returns
101
+ -------
102
+ complex
103
+ """
104
+ z = complex(z)
105
+ return (z - np.sqrt(z**2 - 4)) / 2.0
106
+
107
+
108
+ def r_transform_semicircle(w: float) -> float:
109
+ """Exact R-transform of the standard (variance 1) semicircle law:
110
+ R(w) = w. See module docstring for the direct derivation (from
111
+ ``stieltjes_transform_semicircle``'s own functional inverse) and its
112
+ numerical verification.
113
+
114
+ Parameters
115
+ ----------
116
+ w : float
117
+
118
+ Returns
119
+ -------
120
+ float
121
+ """
122
+ return w
123
+
124
+
125
+ def r_transform_marchenko_pastur(w: float, gamma: float) -> float:
126
+ """R-transform of the Marchenko-Pastur law at aspect ratio gamma:
127
+ R(w) = 1 / (1 - gamma*w). Verified numerically against the exact MP
128
+ density via ``r_transform_numerical`` -- see module docstring.
129
+
130
+ Parameters
131
+ ----------
132
+ w : float
133
+ gamma : float
134
+
135
+ Returns
136
+ -------
137
+ float
138
+ """
139
+ return 1.0 / (1.0 - gamma * w)
140
+
141
+
142
+ def invert_stieltjes_transform_numerical(
143
+ stieltjes_transform: Callable[[complex], complex],
144
+ w: float,
145
+ z_bracket: tuple[float, float],
146
+ ) -> float:
147
+ """Numerically find z such that ``stieltjes_transform(z) == w``, for
148
+ real z in ``z_bracket`` (bisection/Brent's method) -- the functional
149
+ inverse K(w) needed to build an R-transform from an arbitrary
150
+ Stieltjes transform, with no distribution-specific formula assumed.
151
+
152
+ Only valid where the Stieltjes transform is real, finite, and
153
+ monotonic on ``z_bracket`` (e.g. real z strictly outside the
154
+ distribution's support) -- a ``ValueError`` from the underlying
155
+ root-finder (mismatched signs at the bracket ends) generally means
156
+ ``w`` is outside the transform's range on that bracket, not a bug.
157
+
158
+ Parameters
159
+ ----------
160
+ stieltjes_transform : callable
161
+ ``stieltjes_transform(z) -> complex``, real-valued for real z in
162
+ ``z_bracket``.
163
+ w : float
164
+ z_bracket : tuple of float
165
+ ``(z_lo, z_hi)`` search bracket, e.g. just above the
166
+ distribution's upper edge to a large value.
167
+
168
+ Returns
169
+ -------
170
+ float
171
+ """
172
+ z_lo, z_hi = z_bracket
173
+
174
+ def residual(z: float) -> float:
175
+ return stieltjes_transform(z).real - w
176
+
177
+ return brentq(residual, z_lo, z_hi)
178
+
179
+
180
+ def r_transform_numerical(
181
+ stieltjes_transform: Callable[[complex], complex],
182
+ w: float,
183
+ z_bracket: tuple[float, float],
184
+ ) -> float:
185
+ """R-transform at ``w``, computed directly from the definition
186
+ R(w) = K(w) - 1/w, K the functional inverse of the given Stieltjes
187
+ transform (see ``invert_stieltjes_transform_numerical``). Works for
188
+ any Stieltjes transform (empirical or exact), with no
189
+ distribution-specific closed form assumed.
190
+
191
+ Parameters
192
+ ----------
193
+ stieltjes_transform : callable
194
+ w : float
195
+ z_bracket : tuple of float
196
+
197
+ Returns
198
+ -------
199
+ float
200
+ """
201
+ z = invert_stieltjes_transform_numerical(stieltjes_transform, w, z_bracket)
202
+ return z - 1.0 / w
@@ -0,0 +1,338 @@
1
+ """Eigenvector localization statistics: the inverse participation ratio
2
+ (IPR) and its exact value for Haar-distributed (fully delocalized)
3
+ eigenvectors.
4
+
5
+ Unlike every other statistic in this package, these operate on
6
+ eigenVECTORS rather than eigenvalues, so they require a ``Spectrum``
7
+ sampled with ``ensemble.sample(return_eigenvectors=True)`` (see
8
+ ``physicskit.rmt.ensembles.base.MatrixEnsemble.sample``) -- not every ensemble
9
+ supports this (dense diagonalization is required; the fast tridiagonal-
10
+ model ensembles' eigenvalues are exact in distribution but their raw
11
+ eigenvectors are not Haar-distributed, so eigenvector support is only
12
+ implemented where it's been verified to give genuinely Haar-distributed
13
+ (GOE/GUE) or physically meaningful (``PowerLawBandedEnsemble``,
14
+ Anderson localization) eigenvectors -- see each ensemble's docstring).
15
+
16
+ For a normalized eigenvector psi (``sum_i |psi_i|^2 = 1``), the inverse
17
+ participation ratio is
18
+
19
+ ``IPR(psi) = sum_i |psi_i|^4``
20
+
21
+ IPR ~ O(1/n) for a fully delocalized (extended) state spread evenly over
22
+ all n sites, and IPR ~ O(1) (n-independent) for a localized state
23
+ concentrated on a handful of sites -- the standard order parameter for
24
+ the Anderson localization transition.
25
+
26
+ Exact theory for a Haar-distributed (rotationally/unitarily/
27
+ symplectically invariant, i.e. GOE/GUE/GSE-type) unit vector: writing
28
+ each component's squared magnitude as a "beta real dimensions per site"
29
+ weight, ``(|psi_1|^2, ..., |psi_n|^2)`` is exactly Dirichlet(beta/2, ...,
30
+ beta/2) distributed, giving the closed-form moment
31
+
32
+ E[IPR] = (beta/2 + 1) / (n*beta/2 + 1)
33
+
34
+ (reduces to 3/(n+2) at beta=1, 2/(n+1) at beta=2, 3/(2n+1) at beta=4 --
35
+ verified numerically here against direct Haar-vector Monte Carlo at all
36
+ three beta, and independently against the Dirichlet-moment formula's own
37
+ derivation, before being trusted -- see ``tests/test_localization.py``).
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+ from collections.abc import Callable
43
+
44
+ import numpy as np
45
+
46
+ from ..ensembles.base import MatrixEnsemble
47
+
48
+
49
+ def inverse_participation_ratio(eigenvectors: np.ndarray) -> np.ndarray:
50
+ """Inverse participation ratio of each eigenvector (column).
51
+
52
+ Parameters
53
+ ----------
54
+ eigenvectors : numpy.ndarray, shape (..., m, n)
55
+ Eigenvectors as columns (as returned in
56
+ ``Spectrum.eigenvectors``); the leading ``...`` axes (e.g. a
57
+ sample axis) are preserved.
58
+
59
+ Returns
60
+ -------
61
+ numpy.ndarray, shape (..., n)
62
+ IPR of each column, in (0, 1].
63
+ """
64
+ eigenvectors = np.asarray(eigenvectors)
65
+ weights = np.abs(eigenvectors) ** 2
66
+ weights = weights / np.sum(weights, axis=-2, keepdims=True)
67
+ return np.sum(weights**2, axis=-2)
68
+
69
+
70
+ def inverse_participation_ratio_quaternionic(eigenvectors: np.ndarray) -> np.ndarray:
71
+ """Inverse participation ratio for GSE-type (beta=4) eigenvectors,
72
+ folding pairs of complex components into per-site quaternion
73
+ weights first.
74
+
75
+ GSE eigenvectors (as returned by
76
+ ``physicskit.rmt.ensembles.gaussian.GSE.sample(return_eigenvectors=True)``)
77
+ are naturally 2n-dimensional complex vectors representing n
78
+ quaternionic "sites" (rows ``2i, 2i+1`` are one quaternion's two
79
+ complex components) -- calling the plain
80
+ ``inverse_participation_ratio`` on them directly would treat all 2n
81
+ complex components as independent sites, which is not the physically
82
+ meaningful per-site statistic and does not match
83
+ ``ipr_theory(n, beta=4)``. This function folds pairs first:
84
+ ``weight_i = |v[2i]|^2 + |v[2i+1]|^2``, matching the definition used
85
+ to verify ``ipr_theory`` for beta=4 (see
86
+ ``physicskit.rmt.ensembles.gaussian._dense_hermite_quaternion``).
87
+
88
+ Parameters
89
+ ----------
90
+ eigenvectors : numpy.ndarray, shape (..., 2n, n)
91
+ GSE-type eigenvectors as columns (as returned in
92
+ ``Spectrum.eigenvectors`` for ``GSE``).
93
+
94
+ Returns
95
+ -------
96
+ numpy.ndarray, shape (..., n)
97
+ IPR of each column, computed over the n quaternionic sites.
98
+ """
99
+ eigenvectors = np.asarray(eigenvectors)
100
+ weights = np.abs(eigenvectors[..., 0::2, :]) ** 2 + np.abs(eigenvectors[..., 1::2, :]) ** 2
101
+ weights = weights / np.sum(weights, axis=-2, keepdims=True)
102
+ return np.sum(weights**2, axis=-2)
103
+
104
+
105
+ def generalized_ipr(eigenvectors: np.ndarray, q: float) -> np.ndarray:
106
+ """Generalized inverse participation ratio I_q = sum_i p_i^q, for
107
+ normalized site weights ``p_i = |psi_i|^2``. Reduces to
108
+ ``inverse_participation_ratio`` at q=2.
109
+
110
+ The building block for multifractal analysis (see
111
+ ``multifractal_dimension``): at a genuinely multifractal critical
112
+ point (e.g. ``PowerLawBandedEnsemble`` at alpha=1), ``E[I_q]``
113
+ scales as ``n**(-D_q*(q-1))`` for a nontrivial, q-dependent D_q,
114
+ rather than the fully-delocalized ``D_q=1`` (``E[I_q] ~ n**-(q-1)``)
115
+ or fully-localized ``D_q=0`` (``E[I_q]`` roughly n-independent).
116
+
117
+ Parameters
118
+ ----------
119
+ eigenvectors : numpy.ndarray, shape (..., m, n)
120
+ q : float
121
+ Moment order (q != 1; q=1 is the special "information dimension"
122
+ case, requiring a different limiting definition, not implemented
123
+ here).
124
+
125
+ Returns
126
+ -------
127
+ numpy.ndarray, shape (..., n)
128
+ """
129
+ if q == 1:
130
+ raise ValueError("q=1 (information dimension) needs a different limiting definition (based on sum p_i*log(p_i)), not implemented here")
131
+ eigenvectors = np.asarray(eigenvectors)
132
+ weights = np.abs(eigenvectors) ** 2
133
+ weights = weights / np.sum(weights, axis=-2, keepdims=True)
134
+ return np.sum(weights**q, axis=-2)
135
+
136
+
137
+ def mass_exponent(
138
+ ensemble_factory: Callable[..., MatrixEnsemble],
139
+ n_values: list[int],
140
+ q: float,
141
+ n_samples: int = 10,
142
+ seed: int | np.random.Generator | None = None,
143
+ ) -> tuple[float, float]:
144
+ """Estimate the multifractal mass exponent tau(q) by fitting the
145
+ scaling of ``E[generalized_ipr(., q)]`` with system size n:
146
+
147
+ E[I_q](n) ~ n^(-tau(q)) => tau(q) = -slope
148
+
149
+ for a log-log linear regression of mean I_q against n across
150
+ ``n_values``. Unlike ``generalized_ipr``, q=1 is supported directly:
151
+ I_1 = sum_i p_i = 1 identically (site weights are already
152
+ normalized probabilities), so tau(1) = 0 exactly, for any ensemble,
153
+ at any n -- no separate "information dimension" limiting definition
154
+ is needed for tau itself (only for D_q = tau(q)/(q-1), which has a
155
+ removable singularity at q=1).
156
+
157
+ ``multifractal_dimension`` and ``singularity_spectrum`` are both
158
+ built on this.
159
+
160
+ Parameters
161
+ ----------
162
+ ensemble_factory : callable
163
+ ``ensemble_factory(n, seed=...)`` returning a fresh
164
+ ``MatrixEnsemble`` instance of size n that supports
165
+ ``sample(return_eigenvectors=True)``.
166
+ n_values : list of int
167
+ System sizes to sample at, in increasing order.
168
+ q : float
169
+ Moment order.
170
+ n_samples : int, optional
171
+ Independent matrix draws to pool at each n.
172
+ seed : int, numpy.random.Generator, or None, optional
173
+
174
+ Returns
175
+ -------
176
+ tau_q : float
177
+ r_squared : float
178
+ Goodness of fit of the log-log linear regression (close to 1
179
+ indicates clean power-law scaling over the given n_values).
180
+ """
181
+ from scipy.stats import linregress
182
+
183
+ if q == 1.0:
184
+ return 0.0, 1.0
185
+
186
+ rng = np.random.default_rng(seed)
187
+ means = []
188
+ for n in n_values:
189
+ ensemble = ensemble_factory(n, seed=int(rng.integers(1 << 31)))
190
+ spectrum = ensemble.sample(n_samples=n_samples, return_eigenvectors=True)
191
+ assert spectrum.eigenvectors is not None
192
+ means.append(generalized_ipr(spectrum.eigenvectors, q).mean())
193
+ fit = linregress(np.log(n_values), np.log(means))
194
+ return -fit.slope, fit.rvalue**2
195
+
196
+
197
+ def multifractal_dimension(
198
+ ensemble_factory: Callable[..., MatrixEnsemble],
199
+ n_values: list[int],
200
+ q: float,
201
+ n_samples: int = 10,
202
+ seed: int | np.random.Generator | None = None,
203
+ ) -> tuple[float, float]:
204
+ """Estimate the multifractal dimension D_q = tau(q) / (q-1) (see
205
+ ``mass_exponent``). Verified during development against the two
206
+ limiting cases on ``PowerLawBandedEnsemble``: D_2 ~ 1 (delocalized,
207
+ large band-width b) and D_2 ~ 0 (localized, small b), both matching
208
+ to within Monte Carlo noise.
209
+
210
+ Parameters
211
+ ----------
212
+ ensemble_factory : callable
213
+ ``ensemble_factory(n, seed=...)`` returning a fresh
214
+ ``MatrixEnsemble`` instance of size n that supports
215
+ ``sample(return_eigenvectors=True)``.
216
+ n_values : list of int
217
+ System sizes to sample at, in increasing order.
218
+ q : float
219
+ Moment order (q != 1; q=1 is the special "information dimension"
220
+ case, requiring a different limiting definition, not implemented
221
+ here).
222
+ n_samples : int, optional
223
+ Independent matrix draws to pool at each n.
224
+ seed : int, numpy.random.Generator, or None, optional
225
+
226
+ Returns
227
+ -------
228
+ d_q : float
229
+ r_squared : float
230
+ Goodness of fit of the log-log linear regression (close to 1
231
+ indicates clean power-law scaling over the given n_values).
232
+ """
233
+ if q == 1:
234
+ raise ValueError("q=1 (information dimension) needs a different limiting definition (based on sum p_i*log(p_i)), not implemented here")
235
+ tau_q, r_squared = mass_exponent(ensemble_factory, n_values, q, n_samples, seed)
236
+ return tau_q / (q - 1.0), r_squared
237
+
238
+
239
+ def singularity_spectrum(
240
+ ensemble_factory: Callable[..., MatrixEnsemble],
241
+ n_values: list[int],
242
+ q_values: np.ndarray,
243
+ n_samples: int = 10,
244
+ seed: int | np.random.Generator | None = None,
245
+ ) -> tuple[np.ndarray, np.ndarray]:
246
+ """Estimate the multifractal singularity spectrum f(alpha) via the
247
+ Legendre transform of the mass exponent tau(q) (see
248
+ ``mass_exponent``):
249
+
250
+ alpha(q) = d(tau)/dq f(alpha) = q*alpha(q) - tau(q)
251
+
252
+ computed here by evaluating ``mass_exponent`` at each q in
253
+ ``q_values`` and numerically differentiating (``numpy.gradient``)
254
+ the resulting tau(q) curve -- exact given tau(q), but tau(q) itself
255
+ is only as reliable as the underlying finite-size scaling fit,
256
+ which needs a wide enough ``n_values`` range and enough statistics
257
+ per point, especially at large |q| (dominated by rare, extreme
258
+ eigenvector configurations that converge more slowly with n than
259
+ the bulk of the distribution -- a well-known practical difficulty in
260
+ multifractal numerics, not specific to this implementation).
261
+
262
+ Numerically observed during development (``PowerLawBandedEnsemble``
263
+ at its multifractal critical point, alpha=1): f(alpha) comes out
264
+ cleanly positive and concave for q roughly in [0, 1.5], but goes
265
+ negative for q gtrsim 2 at the system sizes/sample counts tractable
266
+ here. This MAY be a genuine "negative dimension" branch (a
267
+ documented phenomenon in multifractal analysis, associated with
268
+ rare fluctuations whose probability decays as a negative power of
269
+ system size) rather than an error, but was not independently
270
+ confirmed against a literature reference value -- treat q gtrsim 2
271
+ results as indicative rather than exact, and prefer a wide
272
+ ``n_values`` range and large ``n_samples`` there.
273
+
274
+ Parameters
275
+ ----------
276
+ ensemble_factory : callable
277
+ n_values : list of int
278
+ q_values : numpy.ndarray
279
+ Moment orders to evaluate tau(q) at (need not exclude q=1).
280
+ n_samples : int, optional
281
+ seed : int, numpy.random.Generator, or None, optional
282
+
283
+ Returns
284
+ -------
285
+ alpha : numpy.ndarray
286
+ f_alpha : numpy.ndarray
287
+ """
288
+ q_values = np.asarray(q_values, dtype=float)
289
+ rng = np.random.default_rng(seed)
290
+ taus = np.empty_like(q_values)
291
+ for i, q in enumerate(q_values):
292
+ sub_seed = int(rng.integers(1 << 31))
293
+ taus[i], _ = mass_exponent(ensemble_factory, n_values, q, n_samples, seed=sub_seed)
294
+ alpha = np.gradient(taus, q_values)
295
+ f_alpha = q_values * alpha - taus
296
+ return alpha, f_alpha
297
+
298
+
299
+ def participation_ratio(eigenvectors: np.ndarray) -> np.ndarray:
300
+ """Participation ratio PR = 1/IPR of each eigenvector (column): the
301
+ effective number of sites a state is spread over -- O(n) for a fully
302
+ delocalized state, O(1) for a localized one.
303
+
304
+ Parameters
305
+ ----------
306
+ eigenvectors : numpy.ndarray, shape (..., m, n)
307
+
308
+ Returns
309
+ -------
310
+ numpy.ndarray, shape (..., n)
311
+ """
312
+ return 1.0 / inverse_participation_ratio(eigenvectors)
313
+
314
+
315
+ def ipr_theory(n: int, beta: float) -> float:
316
+ """Exact mean IPR of a Haar-distributed (fully delocalized) unit
317
+ vector of dimension ``n`` at Dyson index ``beta``:
318
+
319
+ E[IPR] = (beta/2 + 1) / (n*beta/2 + 1)
320
+
321
+ The delocalized reference value that a localized ensemble's
322
+ empirical IPR (see ``inverse_participation_ratio``) is contrasted
323
+ against -- analogous to ``number_variance_poisson`` as the
324
+ uncorrelated baseline for spectral rigidity.
325
+
326
+ Parameters
327
+ ----------
328
+ n : int
329
+ Vector dimension.
330
+ beta : float
331
+ Dyson index (1, 2, or 4 for GOE/GUE/GSE; any beta > 0 valid).
332
+
333
+ Returns
334
+ -------
335
+ float
336
+ """
337
+ alpha = beta / 2.0
338
+ return (alpha + 1.0) / (n * alpha + 1.0)
@@ -0,0 +1,55 @@
1
+ """The Marchenko-Pastur law: limiting eigenvalue density of the Wishart
2
+ ensembles.
3
+
4
+ Reference: V. A. Marchenko, L. A. Pastur, "Distribution of eigenvalues
5
+ for some sets of random matrices", Mat. Sb. 72 (1967) 507.
6
+ """
7
+
8
+ import numpy as np
9
+ from scipy.integrate import quad
10
+
11
+
12
+ def mp_support(gamma: float, sigma2: float = 1.0) -> tuple[float, float]:
13
+ """Support edges [lambda_minus, lambda_plus] of the Marchenko-Pastur
14
+ density, for aspect ratio gamma = n/m (n variables, m samples,
15
+ m >= n so 0 < gamma <= 1) and entry variance sigma2."""
16
+ lo = sigma2 * (1.0 - np.sqrt(gamma)) ** 2
17
+ hi = sigma2 * (1.0 + np.sqrt(gamma)) ** 2
18
+ return lo, hi
19
+
20
+
21
+ def mp_pdf(x: np.ndarray, gamma: float, sigma2: float = 1.0) -> np.ndarray:
22
+ """Marchenko-Pastur probability density at x, for aspect ratio
23
+ gamma = n/m (0 < gamma <= 1; the m >= n regime relevant to the
24
+ ``LaguerreBetaEnsemble`` convention here, which has no point mass at
25
+ zero)."""
26
+ lo, hi = mp_support(gamma, sigma2)
27
+ x = np.asarray(x, dtype=float)
28
+ out = np.zeros_like(x)
29
+ mask = (x > lo) & (x < hi)
30
+ xm = x[mask]
31
+ out[mask] = np.sqrt((hi - xm) * (xm - lo)) / (2.0 * np.pi * gamma * sigma2 * xm)
32
+ return out
33
+
34
+
35
+ def mp_cdf(x: np.ndarray, gamma: float, sigma2: float = 1.0) -> np.ndarray:
36
+ """Marchenko-Pastur CDF at x (0 < gamma <= 1 regime, no point mass).
37
+
38
+ No elementary closed form for general x, so integrated numerically
39
+ from the lower edge; ``scipy.integrate.quad`` is called per point,
40
+ which is fine for the (typically small) grids this is evaluated on
41
+ directly, but ``validation.marchenko_pastur.MarchenkoPastur`` caches
42
+ a fast interpolated version for use inside a KS test.
43
+ """
44
+ lo, hi = mp_support(gamma, sigma2)
45
+ x = np.atleast_1d(np.asarray(x, dtype=float))
46
+ out = np.zeros_like(x)
47
+ for i, xi in enumerate(x):
48
+ if xi <= lo:
49
+ out[i] = 0.0
50
+ elif xi >= hi:
51
+ out[i] = 1.0
52
+ else:
53
+ val, _ = quad(mp_pdf, lo, xi, args=(gamma, sigma2))
54
+ out[i] = val
55
+ return out