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,40 @@
1
+ """Eigenvalue support of a truncated random unitary matrix (see
2
+ ``physicskit.rmt.ensembles.truncated_unitary`` for the construction and the
3
+ numerical verification behind the support-radius formula below).
4
+
5
+ Reference: K. Zyczkowski, H.-J. Sommers, J. Phys. A 33 (2000) 2045.
6
+ """
7
+
8
+ import numpy as np
9
+
10
+
11
+ def truncated_unitary_edge_radius(alpha: float) -> float:
12
+ """Support radius of a truncated unitary matrix's eigenvalues:
13
+ sqrt(alpha), for alpha = m/n the fraction of the full unitary matrix
14
+ kept.
15
+
16
+ Verified numerically (not assumed from memory -- an initial guess
17
+ of sqrt(1-alpha) had exactly the wrong alpha-dependence, caught
18
+ immediately since it would shrink toward 0 as more of the unitary
19
+ matrix is kept, contradicting the alpha=1 exact-unit-circle limit):
20
+ the 99th-percentile empirical eigenvalue radius converges cleanly to
21
+ this value as n grows at fixed alpha -- see
22
+ ``physicskit.rmt.ensembles.truncated_unitary`` module docstring and
23
+ ``tests/test_truncated_unitary.py``.
24
+
25
+ Precision note: this gives only the support EDGE, not the (known to
26
+ be non-uniform, edge-weighted) bulk density within the disk -- see
27
+ module docstring for what was and wasn't verified.
28
+
29
+ Parameters
30
+ ----------
31
+ alpha : float
32
+ Fraction of the full unitary matrix kept, in (0, 1].
33
+
34
+ Returns
35
+ -------
36
+ float
37
+ """
38
+ if not 0.0 < alpha <= 1.0:
39
+ raise ValueError(f"alpha must be in (0, 1], got {alpha}")
40
+ return float(np.sqrt(alpha))
@@ -0,0 +1,37 @@
1
+ """Spectral unfolding: rescale eigenvalues locally so the mean
2
+ nearest-neighbor spacing is 1 everywhere, which is what the spacing
3
+ distribution and rigidity statistics are defined relative to.
4
+
5
+ Where the ensemble's limiting spectral density is known exactly (e.g. the
6
+ semicircle law for the Gaussian ensembles), unfolding via that theoretical
7
+ CDF is preferable to a polynomial/spline fit to the empirical cumulative
8
+ distribution: it introduces no additional fitting noise, and it's exact in
9
+ the same sense the benchmark it will be validated against is exact.
10
+ """
11
+
12
+ from collections.abc import Callable
13
+
14
+ import numpy as np
15
+
16
+
17
+ def unfold_by_cdf(eigenvalues: np.ndarray, cdf_func: Callable[[np.ndarray], np.ndarray]) -> np.ndarray:
18
+ """Unfold a single realization's eigenvalues using a known theoretical
19
+ integrated density (CDF).
20
+
21
+ Parameters
22
+ ----------
23
+ eigenvalues : numpy.ndarray, shape (n,)
24
+ A single realization's eigenvalues (already in the ensemble's
25
+ natural/rescaled units -- e.g. semicircle support [-2, 2]),
26
+ ascending order.
27
+ cdf_func : callable
28
+ The theoretical CDF matching the ensemble's limiting spectral
29
+ density (e.g. ``physicskit.rmt.stats.density.semicircle_cdf``).
30
+
31
+ Returns
32
+ -------
33
+ numpy.ndarray, shape (n,)
34
+ Unfolded eigenvalues with unit mean nearest-neighbor spacing.
35
+ """
36
+ n = len(eigenvalues)
37
+ return n * cdf_func(eigenvalues)
@@ -0,0 +1,67 @@
1
+ """The Wachter distribution: limiting eigenvalue density of the Jacobi
2
+ (MANOVA) ensembles.
3
+
4
+ Reference: K. W. Wachter, "The limiting empirical measure of multiple
5
+ discriminant ratios", Ann. Statist. 8 (1980) 937. Formula and
6
+ parametrization as given in L. Erdos, B. Farrell, "Local Eigenvalue
7
+ Density for General MANOVA Matrices" (arXiv:1207.0031), verified
8
+ numerically during development against real (beta=1) and complex
9
+ (beta=2) Jacobi ensemble simulations (KS statistic < 0.002 in both
10
+ cases at moderate N -- see ``tests/test_jacobi_wachter.py``).
11
+ """
12
+
13
+ import numpy as np
14
+ from scipy.integrate import quad
15
+
16
+
17
+ def wachter_support(a: float, b: float) -> tuple[float, float]:
18
+ """Support edges [lambda_minus, lambda_plus] of the Wachter
19
+ distribution, for inverse aspect ratios a = m1/n, b = m2/n
20
+ (a, b >= 1)."""
21
+ s = a + b
22
+ term1 = np.sqrt((a / s) * (1.0 - 1.0 / s))
23
+ term2 = np.sqrt((1.0 / s) * (1.0 - a / s))
24
+ lam_minus = (term1 - term2) ** 2
25
+ lam_plus = (term1 + term2) ** 2
26
+ return lam_minus, lam_plus
27
+
28
+
29
+ def wachter_pdf(x: np.ndarray, a: float, b: float) -> np.ndarray:
30
+ """Wachter probability density at x, for inverse aspect ratios
31
+ a = m1/n, b = m2/n."""
32
+ lam_minus, lam_plus = wachter_support(a, b)
33
+ x = np.asarray(x, dtype=float)
34
+ out = np.zeros_like(x)
35
+ mask = (x > lam_minus) & (x < lam_plus)
36
+ xm = x[mask]
37
+ out[mask] = (a + b) * np.sqrt((xm - lam_minus) * (lam_plus - xm)) / (2.0 * np.pi * xm * (1.0 - xm))
38
+ return out
39
+
40
+
41
+ def _wachter_pdf_scalar(x: float, a: float, b: float, lam_minus: float, lam_plus: float) -> float:
42
+ # Non-vectorized version for use as a quad integrand (quad calls its
43
+ # integrand with plain Python floats; an array-returning function
44
+ # here caused a real bug during development -- see design notes).
45
+ if x <= lam_minus or x >= lam_plus:
46
+ return 0.0
47
+ return (a + b) * np.sqrt((x - lam_minus) * (lam_plus - x)) / (2.0 * np.pi * x * (1.0 - x))
48
+
49
+
50
+ def wachter_cdf(x: np.ndarray, a: float, b: float) -> np.ndarray:
51
+ """Wachter CDF at x. No elementary closed form, so integrated
52
+ numerically from the lower edge (see
53
+ ``validation.wachter.Wachter`` for a fast interpolated version used
54
+ inside KS tests)."""
55
+ lam_minus, lam_plus = wachter_support(a, b)
56
+ x = np.atleast_1d(np.asarray(x, dtype=float))
57
+ out = np.zeros_like(x)
58
+ for i, xi in enumerate(x):
59
+ xi = float(xi)
60
+ if xi <= lam_minus:
61
+ out[i] = 0.0
62
+ elif xi >= lam_plus:
63
+ out[i] = 1.0
64
+ else:
65
+ val, _ = quad(_wachter_pdf_scalar, lam_minus, xi, args=(a, b, lam_minus, lam_plus))
66
+ out[i] = val
67
+ return out
File without changes
@@ -0,0 +1,107 @@
1
+ """Disk cache for eigenvalue (and eigenvector, when present) data
2
+ produced by sampling a MatrixEnsemble in tests -- avoids re-running
3
+ expensive matrix diagonalization every time the test suite reruns.
4
+
5
+ Usage:
6
+ spectrum = cached_sample(ensemble, n_samples=30)
7
+
8
+ instead of:
9
+ spectrum = ensemble.sample(n_samples=30)
10
+
11
+ Cache key is derived from the ensemble's class name, its public
12
+ constructor-set attributes (n, m, m1, m2, beta, seed, ...), and
13
+ n_samples -- so a fresh cache entry is created whenever any of those
14
+ change, and reruns with identical parameters load straight from disk.
15
+
16
+ NOT used for reproducibility tests that compare two independently
17
+ constructed ensemble instances with the same seed (e.g.
18
+ ``ens_a.sample(...)`` vs ``ens_b.sample(...)``): caching by
19
+ (class, params, seed) would make both calls hit the same cache entry,
20
+ so the second would just re-read the first's cached array rather than
21
+ genuinely resampling -- silently defeating the point of that test. Call
22
+ ``ensemble.sample(...)`` directly there instead.
23
+ """
24
+
25
+ import hashlib
26
+ import json
27
+ from pathlib import Path
28
+
29
+ import numpy as np
30
+
31
+ from physicskit.rmt.spectrum import Spectrum
32
+
33
+ CACHE_DIR = Path(__file__).parent / ".physicskit.rmt_test_cache"
34
+ CACHE_DIR.mkdir(exist_ok=True)
35
+
36
+
37
+ def _public_params(ensemble):
38
+ """Public, JSON-serializable constructor-set attributes of an
39
+ ensemble instance (n, m, m1, m2, beta, seed, ...) -- excludes
40
+ private attributes like ``_rng``."""
41
+ params = {}
42
+ for key, value in vars(ensemble).items():
43
+ if key.startswith("_"):
44
+ continue
45
+ if isinstance(value, (int, float, str, bool)) or value is None:
46
+ params[key] = value
47
+ return params
48
+
49
+
50
+ def _cache_key(ensemble, n_samples):
51
+ payload = json.dumps(
52
+ {
53
+ "cls": type(ensemble).__name__,
54
+ "params": _public_params(ensemble),
55
+ "n_samples": n_samples,
56
+ },
57
+ sort_keys=True,
58
+ )
59
+ return hashlib.sha256(payload.encode()).hexdigest()[:24]
60
+
61
+
62
+ def cached_sample(ensemble, n_samples):
63
+ """Sample ``ensemble`` (or load a matching cached result from disk).
64
+
65
+ Parameters
66
+ ----------
67
+ ensemble : physicskit.rmt.ensembles.base.MatrixEnsemble
68
+ An already-constructed ensemble instance (construction itself is
69
+ cheap; only ``.sample()`` -- the diagonalization -- is cached).
70
+ n_samples : int
71
+
72
+ Returns
73
+ -------
74
+ Spectrum
75
+ """
76
+ key = _cache_key(ensemble, n_samples)
77
+ path = CACHE_DIR / f"{key}.npz"
78
+
79
+ if path.exists():
80
+ data = np.load(path, allow_pickle=False)
81
+ eigenvalues = data["eigenvalues"]
82
+ beta_raw = data["beta"]
83
+ beta = None if np.isnan(beta_raw) else float(beta_raw)
84
+ spectrum = Spectrum(
85
+ eigenvalues=eigenvalues,
86
+ n=int(data["n"]),
87
+ beta=beta,
88
+ ensemble=str(data["ensemble_name"]),
89
+ scale=float(data["scale"]),
90
+ )
91
+ if "eigenvectors" in data.files:
92
+ spectrum.eigenvectors = data["eigenvectors"]
93
+ return spectrum
94
+
95
+ spectrum = ensemble.sample(n_samples=n_samples)
96
+ save_kwargs = dict(
97
+ eigenvalues=spectrum.eigenvalues,
98
+ n=spectrum.n,
99
+ beta=np.nan if spectrum.beta is None else float(spectrum.beta),
100
+ ensemble_name=spectrum.ensemble,
101
+ scale=spectrum.scale,
102
+ )
103
+ eigenvectors = getattr(spectrum, "eigenvectors", None)
104
+ if eigenvectors is not None:
105
+ save_kwargs["eigenvectors"] = eigenvectors
106
+ np.savez(path, **save_kwargs)
107
+ return spectrum
@@ -0,0 +1,8 @@
1
+ """Ensures tests/ is importable as a plain module path (for
2
+ ``from cache_utils import cached_sample``) regardless of how pytest is
3
+ invoked (rootdir vs tests/ as cwd)."""
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ sys.path.insert(0, str(Path(__file__).parent))
@@ -0,0 +1,86 @@
1
+ """Tests for PowerLawBandedEnsemble (PBRM).
2
+
3
+ PBRM has no single universal limiting law to validate against (the
4
+ level statistics themselves are the tunable object of study -- see
5
+ module docstring), so tests check: (1) the variance-profile construction
6
+ matches its own formula directly (the actual thing that could have a
7
+ normalization bug, as one briefly did during development -- an initial
8
+ version halved the target variance by mistakenly reusing GOE's
9
+ two-independent-draws averaging convention for a single-draw
10
+ construction), and (2) the two well-understood limiting regimes behave
11
+ qualitatively as expected (short-range/localized-like vs. GOE-like).
12
+ """
13
+
14
+ import numpy as np
15
+ import pytest
16
+ from cache_utils import cached_sample
17
+
18
+ import physicskit.rmt as rmt
19
+
20
+
21
+ def test_invalid_b_rejected():
22
+ with pytest.raises(ValueError):
23
+ rmt.ensembles.PowerLawBandedEnsemble(n=10, b=0.0, seed=0)
24
+
25
+
26
+ def test_reproducibility():
27
+ ens_a = rmt.ensembles.PowerLawBandedEnsemble(n=40, b=5.0, alpha=1.0, seed=42)
28
+ ens_b = rmt.ensembles.PowerLawBandedEnsemble(n=40, b=5.0, alpha=1.0, seed=42)
29
+ spec_a = ens_a.sample(n_samples=3)
30
+ spec_b = ens_b.sample(n_samples=3)
31
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
32
+
33
+
34
+ def test_default_alpha_is_the_critical_point():
35
+ ens = rmt.ensembles.PowerLawBandedEnsemble(n=10, b=3.0, seed=0)
36
+ assert ens.alpha == 1.0
37
+
38
+
39
+ def test_variance_profile_matches_target_formula():
40
+ # Direct check of the construction's second moment against its own
41
+ # defining formula, pooled over many independent draws -- this is
42
+ # exactly the check that caught an initial /2 normalization bug
43
+ # during development (see module docstring).
44
+ from physicskit.rmt.ensembles.banded import _power_law_variance_profile
45
+
46
+ rng = np.random.default_rng(1)
47
+ n, b, alpha = 20, 5.0, 1.0
48
+ target = _power_law_variance_profile(n, b, alpha)
49
+
50
+ # Build the raw matrix directly (the same way
51
+ # ``PowerLawBandedEnsemble._sample_eigenvalues`` does, bypassing the
52
+ # eigendecomposition) many times to check H_ij's empirical variance
53
+ # against the target profile.
54
+ entries_i_j = []
55
+ for _ in range(30_000):
56
+ variance = target
57
+ std = np.sqrt(variance)
58
+ upper = np.triu(rng.standard_normal((n, n)) * std, k=1)
59
+ h = upper + upper.T
60
+ entries_i_j.append(h[0, 3])
61
+ entries_i_j = np.array(entries_i_j)
62
+ assert entries_i_j.var() == pytest.approx(target[0, 3], rel=0.1)
63
+
64
+
65
+ def test_short_range_limit_looks_localized_narrow_band():
66
+ # alpha very large with a small, fixed band -> effectively a
67
+ # strictly banded (short-range) matrix; the bulk eigenvalue spread
68
+ # should stay modest (order the band width), not grow like a full
69
+ # GOE's sqrt(n).
70
+ n = 800
71
+ ens = rmt.ensembles.PowerLawBandedEnsemble(n=n, b=2.0, alpha=6.0, seed=2)
72
+ spectrum = cached_sample(ens, n_samples=3)
73
+ goe_like_scale = np.sqrt(n)
74
+ assert spectrum.eigenvalues.std() < 0.3 * goe_like_scale
75
+
76
+
77
+ def test_large_alpha_zero_limit_approaches_goe_like_scale():
78
+ # alpha -> 0 makes every entry's variance -> 1 (long-range,
79
+ # effectively unbanded) -- this should look like a GOE matrix with
80
+ # unit-variance entries, i.e. eigenvalue std growing like sqrt(n),
81
+ # not staying bounded the way the short-range case does above.
82
+ n = 800
83
+ ens = rmt.ensembles.PowerLawBandedEnsemble(n=n, b=5.0, alpha=0.01, seed=3)
84
+ spectrum = cached_sample(ens, n_samples=3)
85
+ goe_like_scale = np.sqrt(n)
86
+ assert spectrum.eigenvalues.std() > 0.5 * goe_like_scale
@@ -0,0 +1,29 @@
1
+ """Coverage for physicskit.rmt.ensembles.base.MatrixEnsemble's default
2
+ natural_scale (every concrete ensemble in this codebase overrides it, so a
3
+ minimal dummy subclass is needed to exercise the base implementation)."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import numpy as np
8
+
9
+ from physicskit.rmt.ensembles.base import MatrixEnsemble
10
+
11
+
12
+ class _DummyEnsemble(MatrixEnsemble):
13
+ """Implements only the required abstract method; relies on the base
14
+ class's default natural_scale (1.0, no rescaling)."""
15
+
16
+ def _sample_eigenvalues(self, rng):
17
+ return rng.standard_normal(self.n)
18
+
19
+
20
+ def test_default_natural_scale_is_one():
21
+ ens = _DummyEnsemble(n=4, seed=0)
22
+ assert ens.natural_scale() == 1.0
23
+
24
+
25
+ def test_sample_uses_default_natural_scale_unscaled():
26
+ ens = _DummyEnsemble(n=4, seed=0)
27
+ spectrum = ens.sample(n_samples=3)
28
+ assert spectrum.scale == 1.0
29
+ assert np.all(np.isfinite(spectrum.eigenvalues))
@@ -0,0 +1,133 @@
1
+ """Tests for the Bogoliubov-de Gennes / Altland-Zirnbauer superconductor
2
+ classes D, C, CI, DIII.
3
+
4
+ Unlike most of this package, these are validated only structurally
5
+ (Hermiticity, correct real/complex type per class, and the defining
6
+ +-lambda eigenvalue pairing), not against a closed-form bulk or edge
7
+ density -- see the module docstring in ``physicskit.rmt/ensembles/bdg.py`` for
8
+ why. The constructions themselves come from two independently citable
9
+ sources: Stephanov-Verbaarschot-Wettig (arXiv:hep-ph/0509286, citing
10
+ Altland-Zirnbauer 1997) for classes D and C, and Stolz
11
+ (arXiv:1707.03793, also citing Altland-Zirnbauer) for classes CI and
12
+ DIII, whose definitions are given in purely mathematical terms
13
+ independent of any physics convention -- reducing the risk of a
14
+ convention mismatch relative to earlier, less precisely specified
15
+ sources found during the initial search for this feature.
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
+ BDG_CLASSES = [
25
+ ("D", rmt.ensembles.BdGClassD, 2, False), # (name, cls, beta, expect_real)
26
+ ("C", rmt.ensembles.BdGClassC, 2, False),
27
+ ("CI", rmt.ensembles.BdGClassCI, 1, True),
28
+ ("DIII", rmt.ensembles.BdGClassDIII, 4, False),
29
+ ]
30
+
31
+
32
+ @pytest.mark.parametrize("name,cls,beta,expect_real", BDG_CLASSES)
33
+ def test_beta_is_correct(name, cls, beta, expect_real):
34
+ ens = cls(n=30, seed=0)
35
+ assert ens.beta == beta
36
+
37
+
38
+ @pytest.mark.parametrize("name,cls,beta,expect_real", BDG_CLASSES)
39
+ def test_produces_2n_eigenvalues(name, cls, beta, expect_real):
40
+ ens = cls(n=50, seed=1)
41
+ spectrum = cached_sample(ens, n_samples=5)
42
+ assert spectrum.eigenvalues.shape == (5, 100)
43
+
44
+
45
+ @pytest.mark.parametrize("name,cls,beta,expect_real", BDG_CLASSES)
46
+ def test_eigenvalues_are_real(name, cls, beta, expect_real):
47
+ # All four classes are built from Hermitian matrices, so eigenvalues
48
+ # are always real-valued floats regardless of whether the underlying
49
+ # matrix entries are real (CI) or complex (D, C, DIII).
50
+ ens = cls(n=40, seed=2)
51
+ spectrum = cached_sample(ens, n_samples=5)
52
+ assert not np.iscomplexobj(spectrum.eigenvalues)
53
+ assert np.all(np.isfinite(spectrum.eigenvalues))
54
+
55
+
56
+ @pytest.mark.parametrize("name,cls,beta,expect_real", BDG_CLASSES)
57
+ def test_reproducibility(name, cls, beta, expect_real):
58
+ ens_a = cls(n=40, seed=42)
59
+ ens_b = cls(n=40, seed=42)
60
+ spec_a = ens_a.sample(n_samples=3)
61
+ spec_b = ens_b.sample(n_samples=3)
62
+ np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
63
+
64
+
65
+ @pytest.mark.parametrize("name,cls,beta,expect_real", BDG_CLASSES)
66
+ def test_spectrum_symmetric_about_zero(name, cls, beta, expect_real):
67
+ # The defining particle-hole symmetry: eigenvalues occur in
68
+ # +-lambda pairs (distinct values, not a Kramers-type degeneracy).
69
+ ens = cls(n=60, seed=3)
70
+ spectrum = cached_sample(ens, n_samples=10)
71
+ for row in spectrum.eigenvalues:
72
+ row_sorted = np.sort(row)
73
+ pairing_error = np.abs(row_sorted + row_sorted[::-1]).max()
74
+ assert pairing_error < 1e-8
75
+
76
+
77
+ def test_matrix_construction_hermiticity_and_type_directly():
78
+ # Direct, un-cached structural check of the raw matrix construction
79
+ # (not just the eigenvalues) for each class, including confirming
80
+ # CI is REAL (not just Hermitian) and the other three are complex.
81
+ from physicskit.rmt.ensembles.bdg import (
82
+ _dense_antisymmetric,
83
+ _dense_hermitian,
84
+ _dense_symmetric,
85
+ )
86
+
87
+ rng = np.random.default_rng(7)
88
+ n = 20
89
+
90
+ a = _dense_hermitian(n, rng, complex_entries=True)
91
+ b_d = _dense_antisymmetric(n, rng, complex_entries=True)
92
+ h_d = np.block([[a, b_d], [b_d.conj().T, -a.T]])
93
+ assert np.abs(h_d - h_d.conj().T).max() < 1e-10
94
+ assert np.abs(h_d.imag).max() > 1e-3 # genuinely complex, not real
95
+
96
+ b_c = _dense_symmetric(n, rng, complex_entries=True)
97
+ h_c = np.block([[a, b_c], [b_c.conj().T, -a.T]])
98
+ assert np.abs(h_c - h_c.conj().T).max() < 1e-10
99
+
100
+ x1_ci = _dense_symmetric(n, rng, complex_entries=False)
101
+ x2_ci = _dense_symmetric(n, rng, complex_entries=False)
102
+ h_ci = np.block([[x1_ci, x2_ci], [x2_ci, -x1_ci]])
103
+ assert np.abs(h_ci.imag).max() == 0.0 # exactly real
104
+ assert np.abs(h_ci - h_ci.T).max() < 1e-10
105
+
106
+ y1 = _dense_antisymmetric(n, rng, complex_entries=False)
107
+ y2 = _dense_antisymmetric(n, rng, complex_entries=False)
108
+ x1_diii, x2_diii = 1j * y1, 1j * y2
109
+ h_diii = np.block([[x1_diii, x2_diii], [x2_diii, -x1_diii]])
110
+ assert np.abs(h_diii - h_diii.conj().T).max() < 1e-10
111
+ assert np.abs(h_diii.real).max() == 0.0 # exactly purely-imaginary entries
112
+
113
+ a_real = _dense_hermitian(n, rng, complex_entries=False)
114
+ assert np.abs(a_real.imag).max() == 0.0 # exactly real
115
+ assert np.abs(a_real - a_real.T).max() < 1e-10
116
+
117
+
118
+ def test_all_four_classes_are_structurally_distinct():
119
+ # Sanity check that the four classes don't accidentally collapse to
120
+ # the same construction (a risk given how similar the block forms
121
+ # look on paper).
122
+ n = 30
123
+ spectra = {}
124
+ for name, cls, _beta, _expect_real in BDG_CLASSES:
125
+ ens = cls(n=n, seed=8)
126
+ spectra[name] = np.sort(ens.sample(n_samples=1).eigenvalues[0])
127
+
128
+ # Different beta values (D/C share beta=2 but differ in B's symmetry
129
+ # type; CI and DIII have distinct beta) should generally give
130
+ # different largest-eigenvalue values for the same seed.
131
+ largest = {name: spec[-1] for name, spec in spectra.items()}
132
+ values = list(largest.values())
133
+ assert len(set(np.round(values, 6))) == len(values)
@@ -0,0 +1,50 @@
1
+ """Tests for CUE characteristic polynomial moments (Keating-Snaith).
2
+
3
+ The product formula was verified numerically against direct Monte Carlo
4
+ (Haar-random unitary matrices, evaluated at theta=0) at several (n, k)
5
+ pairs before being trusted -- see
6
+ ``physicskit.rmt/stats/characteristic_polynomial.py`` module docstring.
7
+ """
8
+
9
+ import numpy as np
10
+ import pytest
11
+ from cache_utils import cached_sample
12
+
13
+ import physicskit.rmt as rmt
14
+
15
+
16
+ def test_n1_k1_matches_hand_derivation():
17
+ # 1x1 case: Z_1 = 1 - e^{i(phi-theta)}, E[|Z_1|^2] = 2 - 2*E[cos] = 2
18
+ # exactly (uniform phi over a full period).
19
+ assert rmt.stats.keating_snaith_moment(1, 1) == pytest.approx(2.0)
20
+
21
+
22
+ def test_k1_special_case_is_n_plus_1():
23
+ # Noticed and checked during development: k=1 telescopes to n+1.
24
+ for n in [1, 2, 3, 4, 8]:
25
+ assert rmt.stats.keating_snaith_moment(n, 1) == pytest.approx(n + 1)
26
+
27
+
28
+ def test_rejects_non_positive_k():
29
+ with pytest.raises(ValueError):
30
+ rmt.stats.keating_snaith_moment(5, 0)
31
+ with pytest.raises(ValueError):
32
+ rmt.stats.characteristic_polynomial_empirical_moment(np.zeros((1, 3)), 0)
33
+
34
+
35
+ @pytest.mark.parametrize("n,k", [(2, 1), (3, 1), (4, 1), (2, 2)])
36
+ def test_moment_matches_empirical_cue_simulation(n, k):
37
+ ens = rmt.ensembles.CUE(n=n, seed=0)
38
+ spectrum = cached_sample(ens, n_samples=20000)
39
+ theory = rmt.stats.keating_snaith_moment(n, k)
40
+ empirical = rmt.stats.characteristic_polynomial_empirical_moment(spectrum.eigenvalues, k)
41
+ assert empirical == pytest.approx(theory, rel=0.05)
42
+
43
+
44
+ def test_moments_grow_with_k():
45
+ # Higher moments of a nonnegative random variable are larger (given
46
+ # the variable isn't a.s. constant, which |Z_n| generically isn't).
47
+ n = 4
48
+ m1 = rmt.stats.keating_snaith_moment(n, 1)
49
+ m2 = rmt.stats.keating_snaith_moment(n, 2)
50
+ assert m2 > m1