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,122 @@
1
+ """Tests for the nearest-neighbor spacing distribution, the ratio
2
+ statistic, and their respective surmises.
3
+
4
+ Important methodological distinction from ``test_gaussian_semicircle.py``:
5
+ the Wigner semicircle law is the *exact* N -> infinity limit, so its KS
6
+ distance to a sampled ensemble should shrink toward zero as N grows. The
7
+ Wigner surmise (spacing) and the Atas et al. (2013) surmise (ratio) are
8
+ NOT exact limits -- they are known, very good, but imperfect
9
+ approximations (a fixed ~0.1-1% level discrepancy from the true limiting
10
+ distribution, which involves Fredholm-determinant sine-kernel statistics
11
+ with no elementary closed form). That gap does not vanish as N grows, so
12
+ these tests check "already small at moderate N" rather than "shrinks to
13
+ zero" -- asserting the latter would eventually fail once N is large
14
+ enough for the surmise's own approximation error to dominate over
15
+ finite-size noise, which is expected behavior, not a defect.
16
+ """
17
+
18
+ import numpy as np
19
+ import pytest
20
+ from cache_utils import cached_sample
21
+
22
+ import physicskit.rmt as rmt
23
+
24
+ CLASSICAL = [
25
+ ("GOE", rmt.ensembles.GOE, 1),
26
+ ("GUE", rmt.ensembles.GUE, 2),
27
+ ("GSE", rmt.ensembles.GSE, 4),
28
+ ]
29
+
30
+ # Exact surmise-mean reference values (Atas et al. 2013), NOT the
31
+ # numerically-exact full-ensemble means (which differ at the ~1% level --
32
+ # see docstring in physicskit.rmt/stats/ratios.py).
33
+ RATIO_SURMISE_EXACT_MEANS = {
34
+ 1: 4 - 2 * np.sqrt(3), # 0.535898...
35
+ }
36
+
37
+
38
+ @pytest.mark.parametrize("name,cls,beta", CLASSICAL)
39
+ def test_unfolded_mean_spacing_is_unity(name, cls, beta):
40
+ ens = cls(n=1000, seed=10)
41
+ spectrum = cached_sample(ens, n_samples=30)
42
+ spacings = rmt.stats.nearest_neighbor_spacings(spectrum, rmt.stats.semicircle_cdf)
43
+ assert spacings.mean() == pytest.approx(1.0, abs=0.01)
44
+
45
+
46
+ @pytest.mark.parametrize("name,cls,beta", CLASSICAL)
47
+ def test_spacing_distribution_matches_surmise_at_moderate_n(name, cls, beta):
48
+ # Moderate N and sample count deliberately: pooling an enormous number
49
+ # of spacings would give the KS test enough power to reject on the
50
+ # surmise's own known small approximation error rather than anything
51
+ # wrong with the implementation (see module docstring).
52
+ ens = cls(n=1000, seed=20)
53
+ spectrum = cached_sample(ens, n_samples=20)
54
+ spacings = rmt.stats.nearest_neighbor_spacings(spectrum, rmt.stats.semicircle_cdf)
55
+
56
+ benchmark = rmt.validation.WignerSurmise(beta=beta)
57
+ result = benchmark.validate(spacings, seed=20)
58
+ assert result.ks_statistic < 0.015
59
+ assert result.wasserstein_distance < 0.01
60
+
61
+
62
+ @pytest.mark.parametrize("name,cls,beta", CLASSICAL)
63
+ def test_ratio_statistics_in_unit_interval(name, cls, beta):
64
+ ens = cls(n=500, seed=30)
65
+ spectrum = cached_sample(ens, n_samples=10)
66
+ ratios = rmt.stats.ratio_statistics(spectrum)
67
+ assert np.all(ratios >= 0.0)
68
+ assert np.all(ratios <= 1.0)
69
+
70
+
71
+ @pytest.mark.parametrize("name,cls,beta", CLASSICAL)
72
+ def test_ratio_distribution_matches_surmise_at_moderate_n(name, cls, beta):
73
+ ens = cls(n=1000, seed=40)
74
+ spectrum = cached_sample(ens, n_samples=20)
75
+ ratios = rmt.stats.ratio_statistics(spectrum)
76
+
77
+ benchmark = rmt.validation.RatioDistribution(beta=beta)
78
+ result = benchmark.validate(ratios, seed=40)
79
+ assert result.ks_statistic < 0.02
80
+ assert result.wasserstein_distance < 0.01
81
+
82
+
83
+ def test_ratio_surmise_exact_mean_matches_atas_et_al():
84
+ # The surmise's OWN mean (not the full-ensemble numerical mean) has a
85
+ # known closed form at beta=1: 4 - 2*sqrt(3). This checks the surmise
86
+ # model itself, independent of any sampling.
87
+ model = rmt.stats.RatioSurmise(beta=1)
88
+ grid = np.linspace(0, 1, 200_000)
89
+ mean_r = np.trapezoid(grid * model.pdf(grid), grid)
90
+ assert mean_r == pytest.approx(RATIO_SURMISE_EXACT_MEANS[1], abs=1e-3)
91
+
92
+
93
+ @pytest.mark.parametrize("name,cls,beta", CLASSICAL)
94
+ def test_spacing_and_ratio_agree_independently(name, cls, beta):
95
+ # The spacing distribution requires unfolding; the ratio statistic
96
+ # does not. If an unfolding bug were inflating apparent agreement (or
97
+ # disagreement) in the spacing test, the ratio test -- computed on
98
+ # raw eigenvalues -- would not be affected the same way. Checking
99
+ # both pass together (loosely) guards against that class of bug.
100
+ ens = cls(n=1200, seed=50)
101
+ spectrum = cached_sample(ens, n_samples=20)
102
+
103
+ spacings = rmt.stats.nearest_neighbor_spacings(spectrum, rmt.stats.semicircle_cdf)
104
+ spacing_result = rmt.validation.WignerSurmise(beta=beta).validate(spacings, seed=50)
105
+
106
+ ratios = rmt.stats.ratio_statistics(spectrum)
107
+ ratio_result = rmt.validation.RatioDistribution(beta=beta).validate(ratios, seed=50)
108
+
109
+ assert spacing_result.ks_statistic < 0.02
110
+ assert ratio_result.ks_statistic < 0.02
111
+
112
+
113
+ def test_ratio_statistics_and_spacings_skip_rows_too_short_after_edge_trim():
114
+ # edge_trim=0.5 on a small n leaves fewer than the required minimum
115
+ # eigenvalues per row, so every row is skipped (the "continue" guard)
116
+ # and pooling across zero surviving rows raises downstream.
117
+ ens = rmt.ensembles.GOE(n=5, seed=0)
118
+ spectrum = ens.sample(n_samples=3)
119
+ with pytest.raises(ValueError):
120
+ rmt.stats.ratio_statistics(spectrum, edge_trim=0.5)
121
+ with pytest.raises(ValueError):
122
+ rmt.stats.nearest_neighbor_spacings(spectrum, rmt.stats.semicircle_cdf, edge_trim=0.5)
@@ -0,0 +1,71 @@
1
+ """Tests for the sparse random matrix ensembles (ErdosRenyiEnsemble,
2
+ BernoulliWignerEnsemble).
3
+ """
4
+
5
+ import numpy as np
6
+ import pytest
7
+ from cache_utils import cached_sample
8
+
9
+ import physicskit.rmt as rmt
10
+
11
+
12
+ def test_invalid_p_rejected():
13
+ with pytest.raises(ValueError):
14
+ rmt.ensembles.ErdosRenyiEnsemble(n=10, p=0.0, seed=0)
15
+ with pytest.raises(ValueError):
16
+ rmt.ensembles.BernoulliWignerEnsemble(n=10, p=1.5, seed=0)
17
+
18
+
19
+ def test_erdos_renyi_reproducibility():
20
+ ens_a = rmt.ensembles.ErdosRenyiEnsemble(n=40, p=0.2, seed=42)
21
+ ens_b = rmt.ensembles.ErdosRenyiEnsemble(n=40, p=0.2, seed=42)
22
+ spec_a = ens_a.sample(n_samples=3)
23
+ spec_b = ens_b.sample(n_samples=3)
24
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
25
+
26
+
27
+ def test_erdos_renyi_has_furedi_komlos_outlier():
28
+ # The largest eigenvalue should sit near the mean degree n*p,
29
+ # well separated from the sqrt(n*p*(1-p))-scale bulk.
30
+ n, p = 1500, 0.05
31
+ ens = rmt.ensembles.ErdosRenyiEnsemble(n=n, p=p, seed=1)
32
+ spectrum = cached_sample(ens, n_samples=3)
33
+ mean_degree = n * p
34
+ largest = spectrum.eigenvalues.max(axis=1)
35
+ assert np.all(np.abs(largest - mean_degree) < 0.15 * mean_degree)
36
+
37
+
38
+ def test_erdos_renyi_bulk_approaches_semicircle_scale_when_dense():
39
+ # In the crossover regime (n*p large), the BULK (all but the top
40
+ # outlier) should stay within the semicircle radius
41
+ # 2*sqrt(n*p*(1-p)) up to a modest finite-size margin.
42
+ n, p = 2000, 0.1
43
+ ens = rmt.ensembles.ErdosRenyiEnsemble(n=n, p=p, seed=2)
44
+ spectrum = cached_sample(ens, n_samples=3)
45
+ scale = ens.natural_scale()
46
+ for row in spectrum.eigenvalues:
47
+ bulk = np.sort(row)[:-1] # drop the single largest (outlier)
48
+ assert bulk.max() < 2.2 * scale
49
+ assert bulk.min() > -2.2 * scale
50
+
51
+
52
+ def test_bernoulli_wigner_reproducibility():
53
+ ens_a = rmt.ensembles.BernoulliWignerEnsemble(n=40, p=0.3, seed=42)
54
+ ens_b = rmt.ensembles.BernoulliWignerEnsemble(n=40, p=0.3, seed=42)
55
+ spec_a = ens_a.sample(n_samples=3)
56
+ spec_b = ens_b.sample(n_samples=3)
57
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
58
+
59
+
60
+ @pytest.mark.parametrize("p", [0.3, 0.05, 0.01])
61
+ def test_bernoulli_wigner_matches_semicircle_even_when_sparse(p):
62
+ # No Furedi-Komlos outlier here (mean-zero entries) -- the bulk
63
+ # should match the semicircle law directly, validated against the
64
+ # already-implemented WignerSemicircle benchmark, even at
65
+ # substantial sparsity (as long as n*p stays reasonably large).
66
+ n = 2000
67
+ ens = rmt.ensembles.BernoulliWignerEnsemble(n=n, p=p, seed=3)
68
+ spectrum = cached_sample(ens, n_samples=10)
69
+ benchmark = rmt.validation.WignerSemicircle()
70
+ result = benchmark.validate(spectrum, seed=3)
71
+ assert result.ks_statistic < 0.03
@@ -0,0 +1,27 @@
1
+ """Coverage for physicskit.rmt.spectrum.Spectrum's simple derived properties
2
+ and __repr__ (previously never exercised directly)."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import numpy as np
7
+
8
+ import physicskit.rmt as rmt
9
+
10
+
11
+ def test_flat_ravels_all_samples():
12
+ ens = rmt.ensembles.GOE(n=5, seed=0)
13
+ spectrum = ens.sample(n_samples=3)
14
+ assert spectrum.flat.shape == (15,)
15
+ np.testing.assert_allclose(spectrum.flat, spectrum.eigenvalues.ravel())
16
+
17
+
18
+ def test_n_samples_matches_first_axis():
19
+ ens = rmt.ensembles.GOE(n=5, seed=0)
20
+ spectrum = ens.sample(n_samples=4)
21
+ assert spectrum.n_samples == 4
22
+
23
+
24
+ def test_repr_contains_ensemble_name_and_n_samples():
25
+ ens = rmt.ensembles.GOE(n=5, seed=0)
26
+ spectrum = ens.sample(n_samples=2)
27
+ assert repr(spectrum) == f"Spectrum(ensemble='GOE', n=5, beta={spectrum.beta}, n_samples=2)"
@@ -0,0 +1,136 @@
1
+ """Tests for the Sachdev-Ye-Kitaev (SYK) ensemble.
2
+
3
+ Unlike every other ensemble in this package, SYK is not built from
4
+ i.i.d. matrix entries -- it's an explicit many-body Hamiltonian on an
5
+ exponentially large (2**(N/2)-dimensional) Hilbert space. Validation
6
+ here is therefore structural (Clifford algebra, Hermiticity, the exact
7
+ algebraic reason q=4 needs no phase but q=2 needs a factor of i) rather
8
+ than against a closed-form limiting spectral density, which -- unlike
9
+ the semicircle/Marchenko-Pastur/circular laws elsewhere in this package
10
+ -- has no simple exact form for general q at finite N. Kept to small N
11
+ throughout (cost is exponential in N, not polynomial -- see the module
12
+ docstring).
13
+ """
14
+
15
+ import math
16
+
17
+ import numpy as np
18
+ import pytest
19
+ from cache_utils import cached_sample
20
+
21
+ import physicskit.rmt as rmt
22
+ from physicskit.rmt.ensembles.syk import _sample_syk_hamiltonian, majorana_operators
23
+
24
+
25
+ @pytest.mark.parametrize("n", [4, 6, 8])
26
+ def test_majorana_operators_are_hermitian(n):
27
+ for gamma in majorana_operators(n):
28
+ assert np.abs(gamma - gamma.conj().T).max() < 1e-12
29
+
30
+
31
+ @pytest.mark.parametrize("n", [4, 6, 8])
32
+ def test_majorana_operators_satisfy_clifford_algebra(n):
33
+ gammas = majorana_operators(n)
34
+ dim = gammas[0].shape[0]
35
+ identity = np.eye(dim)
36
+ for a in range(n):
37
+ for b in range(n):
38
+ anticommutator = gammas[a] @ gammas[b] + gammas[b] @ gammas[a]
39
+ target = 2.0 * identity if a == b else np.zeros((dim, dim))
40
+ assert np.abs(anticommutator - target).max() < 1e-10
41
+
42
+
43
+ def test_majorana_operators_reject_odd_n():
44
+ with pytest.raises(ValueError):
45
+ majorana_operators(5)
46
+
47
+
48
+ @pytest.mark.parametrize("q,should_need_phase", [(2, True), (4, False), (6, True), (8, False)])
49
+ def test_raw_q_body_product_hermiticity_matches_derivation(q, should_need_phase):
50
+ # Reversing q distinct anticommuting Majoranas picks up sign
51
+ # (-1)**(q*(q-1)/2) -- the raw (no i**(q/2) prefactor) product should
52
+ # be Hermitian exactly when that sign is +1, i.e. exactly when
53
+ # should_need_phase is False. This is the algebraic fact the
54
+ # i**(q/2) prefactor in the module docstring is derived from;
55
+ # checked directly here rather than only trusted from the derivation.
56
+ n = 8
57
+ gammas = majorana_operators(n)
58
+ idx = list(range(q))
59
+ term = gammas[idx[0]]
60
+ for i in idx[1:]:
61
+ term = term @ gammas[i]
62
+ is_hermitian = np.abs(term - term.conj().T).max() < 1e-10
63
+ assert is_hermitian == (not should_need_phase)
64
+ # With the i**(q/2) prefactor applied, it should always be Hermitian.
65
+ phased = (1j ** (q // 2)) * term
66
+ assert np.abs(phased - phased.conj().T).max() < 1e-10
67
+
68
+
69
+ def test_beta_is_none():
70
+ ens = rmt.ensembles.SYKEnsemble(n=8, seed=0)
71
+ assert ens.beta is None
72
+
73
+
74
+ def test_default_q_is_four():
75
+ ens = rmt.ensembles.SYKEnsemble(n=8, seed=0)
76
+ assert ens.q == 4
77
+
78
+
79
+ @pytest.mark.parametrize("n", [4, 6, 8, 10])
80
+ def test_produces_2_pow_n_over_2_real_eigenvalues(n):
81
+ ens = rmt.ensembles.SYKEnsemble(n=n, seed=1)
82
+ spectrum = cached_sample(ens, n_samples=3)
83
+ assert spectrum.eigenvalues.shape == (3, 2 ** (n // 2))
84
+ assert not np.iscomplexobj(spectrum.eigenvalues)
85
+ assert np.all(np.isfinite(spectrum.eigenvalues))
86
+
87
+
88
+ def test_reproducibility():
89
+ ens_a = rmt.ensembles.SYKEnsemble(n=8, seed=42)
90
+ ens_b = rmt.ensembles.SYKEnsemble(n=8, seed=42)
91
+ spec_a = ens_a.sample(n_samples=3)
92
+ spec_b = ens_b.sample(n_samples=3)
93
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
94
+
95
+
96
+ def test_invalid_parameters_rejected():
97
+ with pytest.raises(ValueError):
98
+ rmt.ensembles.SYKEnsemble(n=7, seed=0) # odd n
99
+ with pytest.raises(ValueError):
100
+ rmt.ensembles.SYKEnsemble(n=8, q=3, seed=0) # odd q
101
+ with pytest.raises(ValueError):
102
+ rmt.ensembles.SYKEnsemble(n=8, q=10, seed=0) # q > n
103
+
104
+
105
+ def test_q_two_produces_a_finite_nondegenerate_free_fermion_spectrum():
106
+ # q=2 is the (non-chaotic) free-fermion special case: H is built
107
+ # from a single random N x N antisymmetric coupling matrix rather
108
+ # than C(N,4) independent ones. Just check it produces a sane,
109
+ # genuinely nonzero-scale spectrum -- not a specific numeric target,
110
+ # since the exact eigenvalue scale for q=2 (checked empirically
111
+ # during development to grow roughly like sqrt(N), not stay O(1) the
112
+ # way q=4's does) isn't asserted here.
113
+ ens = rmt.ensembles.SYKEnsemble(n=10, q=2, coupling=1.0, seed=3)
114
+ spectrum = cached_sample(ens, n_samples=20)
115
+ assert np.all(np.isfinite(spectrum.eigenvalues))
116
+ assert spectrum.eigenvalues.std() > 0.1
117
+
118
+
119
+ def test_coupling_scales_hamiltonian_linearly():
120
+ n, q = 8, 4
121
+ gammas = majorana_operators(n)
122
+ h1 = _sample_syk_hamiltonian(n, q, 1.0, np.random.default_rng(5), gammas)
123
+ h2 = _sample_syk_hamiltonian(n, q, 2.0, np.random.default_rng(5), gammas)
124
+ np.testing.assert_allclose(h2, 2.0 * h1)
125
+
126
+
127
+ def test_variance_normalization_matches_maldacena_stanford():
128
+ # <J_{i1...iq}^2> = (q-1)! * J^2 / N^(q-1) -- checked directly against
129
+ # the empirical variance of many independently drawn couplings,
130
+ # rather than only trusted from the module docstring's citation.
131
+ n, q, coupling = 12, 4, 1.5
132
+ rng = np.random.default_rng(6)
133
+ expected_variance = math.factorial(q - 1) * coupling**2 / n ** (q - 1)
134
+ std = math.sqrt(expected_variance)
135
+ draws = rng.normal(0.0, std, size=200_000)
136
+ assert draws.var() == pytest.approx(expected_variance, rel=0.05)
@@ -0,0 +1,113 @@
1
+ """Tests for the Painleve II solution and the Tracy-Widom soft-edge
2
+ benchmark.
3
+
4
+ Two things were determined empirically during development, not assumed
5
+ from memory, and are worth testing explicitly here rather than only in
6
+ the module docstring: (1) q(x)'s known asymptotic tail
7
+ q(x) ~ sqrt(-x/2) as x -> -infinity, and (2) the beta=4 (GSE) edge
8
+ scaling requires (2n)^(2/3), not n^(2/3) -- confirmed by direct
9
+ comparison against Monte Carlo GSE samples via a KS test, since the
10
+ half-remembered formula did NOT match without this factor.
11
+
12
+ Note on statistical power: the largest eigenvalue is a single
13
+ extreme-value statistic per matrix sample, so pooling matters much less
14
+ than the bulk-statistic benchmarks (each sample of GOE/GUE/GSE
15
+ contributes exactly one data point to the edge-statistic distribution).
16
+ n_samples in these tests is correspondingly large (hundreds), unlike
17
+ elsewhere in the test suite.
18
+ """
19
+
20
+ import numpy as np
21
+ import pytest
22
+ from cache_utils import cached_sample
23
+
24
+ import physicskit.rmt as rmt
25
+
26
+
27
+ def test_painleve_ii_matches_known_asymptotic_tail():
28
+ # Re-derive q via the same ODE construction used internally, at a
29
+ # couple of points in the asymptotic regime, and compare to the
30
+ # known analytic tail sqrt(-x/2).
31
+ from scipy.integrate import solve_ivp
32
+ from scipy.special import airy
33
+
34
+ def rhs(x, y):
35
+ qv, qp = y
36
+ return [qp, x * qv + 2.0 * qv**3]
37
+
38
+ x0 = 6.0
39
+ ai, aip, _, _ = airy(x0)
40
+ ivp = solve_ivp(rhs, [x0, -6.0], [ai, aip], dense_output=True, rtol=1e-12, atol=1e-14, max_step=0.005)
41
+ for x in [-6.0, -5.0, -4.0]:
42
+ q_val = ivp.sol(x)[0]
43
+ asymptotic = np.sqrt(-x / 2.0)
44
+ assert q_val == pytest.approx(asymptotic, rel=0.01)
45
+
46
+
47
+ def test_cdfs_are_monotonic_and_span_zero_to_one():
48
+ grid = np.linspace(-6, 6, 200)
49
+ for beta in (1, 2, 4):
50
+ cdf = rmt.stats.tracy_widom_cdf(grid, beta)
51
+ assert np.all(np.diff(cdf) >= -1e-10) # monotone non-decreasing
52
+ assert cdf[0] < 0.01
53
+ assert cdf[-1] > 0.99
54
+
55
+
56
+ def test_unsupported_beta_rejected():
57
+ with pytest.raises(ValueError):
58
+ rmt.stats.tracy_widom_cdf(0.0, beta=1.7)
59
+ with pytest.raises(ValueError):
60
+ rmt.validation.TracyWidom(beta=3)
61
+
62
+
63
+ def test_edge_scale_beta4_has_extra_factor_of_two():
64
+ n = 500
65
+ scale_1 = rmt.stats.tracy_widom_edge_scale(n, beta=1)
66
+ scale_2 = rmt.stats.tracy_widom_edge_scale(n, beta=2)
67
+ scale_4 = rmt.stats.tracy_widom_edge_scale(n, beta=4)
68
+ assert scale_1 == pytest.approx(n ** (2 / 3))
69
+ assert scale_2 == pytest.approx(n ** (2 / 3))
70
+ assert scale_4 == pytest.approx((2 * n) ** (2 / 3))
71
+ assert scale_4 != pytest.approx(scale_1) # the easy-to-miss distinction
72
+
73
+
74
+ def test_edge_scale_and_rvs_reject_unsupported_beta():
75
+ with pytest.raises(ValueError):
76
+ rmt.stats.tracy_widom_edge_scale(100, beta=3)
77
+ with pytest.raises(ValueError):
78
+ rmt.stats.tracy_widom_rvs(5, beta=3, rng=np.random.default_rng(0))
79
+
80
+
81
+ GAUSSIAN_TW = [
82
+ ("GOE", rmt.ensembles.GOE, 1),
83
+ ("GUE", rmt.ensembles.GUE, 2),
84
+ ("GSE", rmt.ensembles.GSE, 4),
85
+ ]
86
+
87
+
88
+ @pytest.mark.parametrize("name,cls,beta", GAUSSIAN_TW)
89
+ def test_largest_eigenvalue_matches_tracy_widom(name, cls, beta):
90
+ ens = cls(n=500, seed=10)
91
+ spectrum = cached_sample(ens, n_samples=400)
92
+ benchmark = rmt.validation.TracyWidom(beta=beta)
93
+ result = benchmark.validate(spectrum, seed=10)
94
+ assert result.ks_statistic < 0.08
95
+ assert result.ks_pvalue > 0.05
96
+
97
+
98
+ @pytest.mark.parametrize("name,cls,beta", GAUSSIAN_TW)
99
+ def test_beta_mismatch_rejected(name, cls, beta):
100
+ other_beta = {1: 2, 2: 4, 4: 1}[beta]
101
+ ens = cls(n=100, seed=0)
102
+ spectrum = cached_sample(ens, n_samples=20)
103
+ benchmark = rmt.validation.TracyWidom(beta=other_beta)
104
+ with pytest.raises(ValueError):
105
+ benchmark.validate(spectrum)
106
+
107
+
108
+ def test_largest_eigenvalues_helper():
109
+ ens = rmt.ensembles.GUE(n=200, seed=0)
110
+ spectrum = cached_sample(ens, n_samples=10)
111
+ lam_max = rmt.stats.largest_eigenvalues(spectrum)
112
+ assert lam_max.shape == (10,)
113
+ np.testing.assert_allclose(lam_max, spectrum.rescaled.max(axis=1))
@@ -0,0 +1,41 @@
1
+ """Coverage for physicskit.rmt.utils.tridiagonal's input-validation branches
2
+ (each function is exercised through the ensembles that call it, but never
3
+ with an invalid n/m that should raise)."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import numpy as np
8
+ import pytest
9
+
10
+ from physicskit.rmt.utils.tridiagonal import (
11
+ sample_hermite_beta_eigenvalues,
12
+ sample_hermite_beta_eigenvalues_and_vectors,
13
+ sample_laguerre_beta_eigenvalues,
14
+ )
15
+
16
+
17
+ def test_sample_hermite_beta_eigenvalues_rejects_n_below_2():
18
+ with pytest.raises(ValueError):
19
+ sample_hermite_beta_eigenvalues(1, beta=1, rng=np.random.default_rng(0))
20
+
21
+
22
+ def test_sample_hermite_beta_eigenvalues_and_vectors_rejects_n_below_2():
23
+ with pytest.raises(ValueError):
24
+ sample_hermite_beta_eigenvalues_and_vectors(1, beta=1, rng=np.random.default_rng(0))
25
+
26
+
27
+ def test_sample_hermite_beta_eigenvalues_and_vectors_returns_matching_shapes():
28
+ n = 12
29
+ eigenvalues, eigenvectors = sample_hermite_beta_eigenvalues_and_vectors(n, beta=1, rng=np.random.default_rng(1))
30
+ assert eigenvalues.shape == (n,)
31
+ assert eigenvectors.shape == (n, n)
32
+ assert np.all(np.diff(eigenvalues) >= 0) # ascending
33
+ # eigenvectors should be orthonormal columns
34
+ np.testing.assert_allclose(eigenvectors.T @ eigenvectors, np.eye(n), atol=1e-8)
35
+
36
+
37
+ def test_sample_laguerre_beta_eigenvalues_rejects_n_below_2_and_m_below_n():
38
+ with pytest.raises(ValueError):
39
+ sample_laguerre_beta_eigenvalues(m=10, n=1, beta=2, rng=np.random.default_rng(0))
40
+ with pytest.raises(ValueError):
41
+ sample_laguerre_beta_eigenvalues(m=5, n=10, beta=2, rng=np.random.default_rng(0))
@@ -0,0 +1,80 @@
1
+ """Tests for TruncatedUnitaryEnsemble and its support-radius formula.
2
+
3
+ The support radius sqrt(alpha) was NOT assumed from memory -- an initial
4
+ guess of sqrt(1-alpha) had exactly the wrong alpha-dependence (shrinking
5
+ as more of the unitary matrix is kept, contradicting the alpha=1 exact-
6
+ unit-circle limit), caught by checking the direction of the trend before
7
+ committing to a formula. The correct formula was then confirmed via the
8
+ 99th-percentile empirical eigenvalue radius converging cleanly to it as
9
+ n grows at fixed alpha -- see
10
+ ``physicskit.rmt/ensembles/truncated_unitary.py`` module docstring.
11
+ """
12
+
13
+ import numpy as np
14
+ import pytest
15
+ from cache_utils import cached_sample
16
+
17
+ import physicskit.rmt as rmt
18
+
19
+
20
+ def test_invalid_alpha_rejected():
21
+ with pytest.raises(ValueError):
22
+ rmt.ensembles.TruncatedUnitaryEnsemble(m=10, alpha=0.0, seed=0)
23
+ with pytest.raises(ValueError):
24
+ rmt.ensembles.TruncatedUnitaryEnsemble(m=10, alpha=1.5, seed=0)
25
+ with pytest.raises(ValueError):
26
+ rmt.stats.truncated_unitary_edge_radius(0.0)
27
+
28
+
29
+ def test_alpha_1_recovers_unit_circle():
30
+ # A full (untruncated) Haar-unitary matrix's eigenvalues lie exactly
31
+ # on the unit circle.
32
+ ens = rmt.ensembles.TruncatedUnitaryEnsemble(m=100, alpha=1.0, seed=0)
33
+ spectrum = cached_sample(ens, n_samples=5)
34
+ radii = np.abs(spectrum.eigenvalues.ravel())
35
+ np.testing.assert_allclose(radii, 1.0, atol=1e-8)
36
+
37
+
38
+ def test_edge_radius_matches_formula():
39
+ for alpha in [0.2, 0.5, 0.8]:
40
+ assert rmt.stats.truncated_unitary_edge_radius(alpha) == pytest.approx(np.sqrt(alpha))
41
+
42
+
43
+ def test_reproducibility():
44
+ ens_a = rmt.ensembles.TruncatedUnitaryEnsemble(m=40, alpha=0.5, seed=42)
45
+ ens_b = rmt.ensembles.TruncatedUnitaryEnsemble(m=40, alpha=0.5, seed=42)
46
+ spec_a = ens_a.sample(n_samples=3)
47
+ spec_b = ens_b.sample(n_samples=3)
48
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
49
+
50
+
51
+ def test_edge_radius_converges_with_increasing_m():
52
+ # The 99th-percentile RESCALED radius (spectrum.rescaled already
53
+ # divides by natural_scale() = sqrt(alpha), so the target here is
54
+ # 1.0, not sqrt(alpha) again) should approach 1 as the truncated
55
+ # block size (and hence the full unitary dimension, at fixed alpha)
56
+ # grows -- checked here via a shrinking overshoot rather than a
57
+ # fixed tolerance at one size.
58
+ alpha = 0.5
59
+ overshoots = []
60
+ for m in [50, 100, 200, 400]:
61
+ ens = rmt.ensembles.TruncatedUnitaryEnsemble(m=m, alpha=alpha, seed=1)
62
+ spectrum = cached_sample(ens, n_samples=30)
63
+ radii = np.abs(spectrum.rescaled.ravel())
64
+ p99 = np.percentile(radii, 99)
65
+ overshoots.append(p99 - 1.0)
66
+ assert overshoots[0] > overshoots[-1] > -0.05
67
+ assert overshoots[-1] < 0.05
68
+
69
+
70
+ def test_bulk_density_is_not_uniform_like_ginibre():
71
+ # Qualitative check of the module docstring's claim: mean radius
72
+ # should exceed a uniform disk's (2/3)*edge prediction, i.e. the
73
+ # density is weighted toward the edge, unlike the circular law's
74
+ # flat disk.
75
+ alpha = 0.5
76
+ ens = rmt.ensembles.TruncatedUnitaryEnsemble(m=300, alpha=alpha, seed=2)
77
+ spectrum = cached_sample(ens, n_samples=30)
78
+ radii = np.abs(spectrum.rescaled.ravel())
79
+ uniform_disk_mean = (2.0 / 3.0) * rmt.stats.truncated_unitary_edge_radius(alpha)
80
+ assert radii.mean() > uniform_disk_mean
@@ -0,0 +1,25 @@
1
+ """Coverage for physicskit.rmt.validation.base: Benchmark.convergence_curve's
2
+ generic (non-overridden) implementation and ValidationResult.__repr__ --
3
+ every existing test that mentions convergence_curve deliberately loops
4
+ manually instead, so the base implementation itself was never called."""
5
+
6
+ from __future__ import annotations
7
+
8
+ import numpy as np
9
+
10
+ import physicskit.rmt as rmt
11
+
12
+
13
+ def test_convergence_curve_generic_implementation_runs_and_shrinks():
14
+ benchmark = rmt.validation.WignerSemicircle()
15
+ results = benchmark.convergence_curve(lambda n, seed: rmt.ensembles.GOE(n=n, seed=seed), n_values=[20, 60], n_samples=5, seed=0)
16
+ assert [n for n, _ in results] == [20, 60]
17
+ ks_20 = results[0][1].ks_statistic
18
+ ks_60 = results[1][1].ks_statistic
19
+ assert ks_60 < ks_20
20
+
21
+
22
+ def test_validation_result_repr():
23
+ result = rmt.validation.base.ValidationResult(ks_statistic=0.01234, ks_pvalue=0.5, wasserstein_distance=0.005, n_eigenvalues=100)
24
+ assert repr(result) == "ValidationResult(ks_statistic=0.01234, ks_pvalue=0.5, wasserstein_distance=0.00500, n_eigenvalues=100)"
25
+ assert np.isfinite(result.ks_statistic)
@@ -0,0 +1,67 @@
1
+ """Coverage for a handful of never-exercised convenience methods across
2
+ physicskit.rmt.validation.*: each Benchmark subclass's theoretical_pdf
3
+ (only theoretical_cdf is used internally elsewhere), the sine_kernel and
4
+ real_ginibre result types' __repr__, UniversalityResult.__repr__, and the
5
+ circular_law/tracy_widom convergence_curve overrides (every existing test
6
+ that mentions them deliberately loops manually instead)."""
7
+
8
+ from __future__ import annotations
9
+
10
+ import numpy as np
11
+
12
+ import physicskit.rmt as rmt
13
+ from physicskit.rmt.validation.real_ginibre import RealEigenvalueCountResult
14
+ from physicskit.rmt.validation.sine_kernel import CorrelationValidationResult
15
+ from physicskit.rmt.validation.universality import UniversalityResult
16
+
17
+
18
+ def test_theoretical_pdf_methods_are_finite_and_nonnegative():
19
+ cases = [
20
+ (rmt.validation.MarchenkoPastur(gamma=0.5), np.linspace(0.1, 2.0, 10)),
21
+ (rmt.validation.Wachter(a=1.0, b=1.0), np.linspace(0.1, 0.9, 10)),
22
+ (rmt.validation.WignerSemicircle(), np.linspace(-1.5, 1.5, 10)),
23
+ (rmt.validation.SingleRingTheorem(r_in=0.5, r_out=1.0), np.linspace(0.5, 1.0, 10)),
24
+ (rmt.validation.CircularLaw(), np.linspace(0.1, 0.9, 10)),
25
+ ]
26
+ for benchmark, x in cases:
27
+ pdf = benchmark.theoretical_pdf(x)
28
+ assert np.all(np.isfinite(pdf))
29
+ assert np.all(pdf >= 0.0)
30
+
31
+
32
+ def test_ratio_distribution_and_wigner_surmise_theoretical_pdf():
33
+ ratio = rmt.validation.RatioDistribution(beta=2)
34
+ r = np.linspace(0.0, 5.0, 10)
35
+ assert np.all(np.isfinite(ratio.theoretical_pdf(r)))
36
+
37
+ surmise = rmt.validation.WignerSurmise(beta=2)
38
+ s = np.linspace(0.0, 3.0, 10)
39
+ assert np.all(np.isfinite(surmise.theoretical_pdf(s)))
40
+
41
+
42
+ def test_correlation_validation_result_repr():
43
+ result = CorrelationValidationResult(rmse=0.01234, r_max=4.0, n_bins=60)
44
+ assert repr(result) == "CorrelationValidationResult(rmse=0.01234, r_max=4.0, n_bins=60)"
45
+
46
+
47
+ def test_real_eigenvalue_count_result_repr_and_relative_error():
48
+ result = RealEigenvalueCountResult(empirical_mean=8.0, theoretical=10.0, n=100)
49
+ assert result.relative_error == 0.2
50
+ assert repr(result) == "RealEigenvalueCountResult(empirical_mean=8.0000, theoretical=10.0000, n=100, relative_error=0.2000)"
51
+
52
+
53
+ def test_universality_result_repr():
54
+ result = UniversalityResult(per_distribution={"uniform": rmt.validation.base.ValidationResult(0.01, 0.5, 0.02, 50)}, max_ks_statistic=0.01)
55
+ text = repr(result)
56
+ assert text.startswith("UniversalityResult(\n")
57
+ assert "max_ks=0.01000" in text
58
+
59
+
60
+ def test_circular_law_and_tracy_widom_convergence_curve_overrides():
61
+ circ = rmt.validation.CircularLaw()
62
+ results_c = circ.convergence_curve(lambda n, seed: rmt.ensembles.GinUE(n=n, seed=seed), n_values=[20, 40], n_samples=5, seed=0)
63
+ assert [n for n, _ in results_c] == [20, 40]
64
+
65
+ tw = rmt.validation.TracyWidom(beta=2)
66
+ results_tw = tw.convergence_curve(lambda n, seed: rmt.ensembles.GUE(n=n, seed=seed), n_values=[20, 40], n_samples=10, seed=0)
67
+ assert [n for n, _ in results_tw] == [20, 40]