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.
- physicskit/__init__.py +51 -0
- physicskit/astro/__init__.py +66 -0
- physicskit/astro/cosmic_web.py +395 -0
- physicskit/astro/galactic_dynamics.py +137 -0
- physicskit/astro/nbody.py +208 -0
- physicskit/astro/orbital_mechanics.py +220 -0
- physicskit/astro/stellar_dynamo.py +628 -0
- physicskit/astro/stellar_structure.py +198 -0
- physicskit/astro/tests/__init__.py +0 -0
- physicskit/astro/tests/conftest.py +7 -0
- physicskit/astro/tests/test_cosmic_web.py +159 -0
- physicskit/astro/tests/test_galactic_dynamics.py +46 -0
- physicskit/astro/tests/test_nbody.py +81 -0
- physicskit/astro/tests/test_orbital_mechanics.py +73 -0
- physicskit/astro/tests/test_stellar_dynamo.py +198 -0
- physicskit/astro/tests/test_stellar_structure.py +52 -0
- physicskit/astro/tests/test_visualizers.py +159 -0
- physicskit/astro/visualizers.py +628 -0
- physicskit/chaos/__init__.py +55 -0
- physicskit/chaos/core/__init__.py +3 -0
- physicskit/chaos/core/base_system.py +342 -0
- physicskit/chaos/core/integrators.py +40 -0
- physicskit/chaos/exceptions.py +24 -0
- physicskit/chaos/quantum/__init__.py +38 -0
- physicskit/chaos/quantum/billiards.py +274 -0
- physicskit/chaos/quantum/husimi.py +85 -0
- physicskit/chaos/quantum/maps.py +366 -0
- physicskit/chaos/systems/__init__.py +38 -0
- physicskit/chaos/systems/billiards.py +1131 -0
- physicskit/chaos/systems/continuous.py +1276 -0
- physicskit/chaos/systems/maps.py +557 -0
- physicskit/chaos/tests/__init__.py +0 -0
- physicskit/chaos/tests/test_basins.py +76 -0
- physicskit/chaos/tests/test_bifurcation.py +132 -0
- physicskit/chaos/tests/test_billiards.py +115 -0
- physicskit/chaos/tests/test_continuous.py +224 -0
- physicskit/chaos/tests/test_dimension.py +69 -0
- physicskit/chaos/tests/test_divergence.py +90 -0
- physicskit/chaos/tests/test_dynamic_plots.py +269 -0
- physicskit/chaos/tests/test_integrators.py +96 -0
- physicskit/chaos/tests/test_io.py +41 -0
- physicskit/chaos/tests/test_maps.py +110 -0
- physicskit/chaos/tests/test_metrics.py +115 -0
- physicskit/chaos/tests/test_phase_space.py +73 -0
- physicskit/chaos/tests/test_properties.py +116 -0
- physicskit/chaos/tests/test_quantum_billiards.py +94 -0
- physicskit/chaos/tests/test_quantum_maps.py +144 -0
- physicskit/chaos/tests/test_recurrence.py +51 -0
- physicskit/chaos/tests/test_repr_and_api_consistency.py +134 -0
- physicskit/chaos/tests/test_section.py +49 -0
- physicskit/chaos/tests/test_spectral.py +48 -0
- physicskit/chaos/tests/test_streamlit_app.py +45 -0
- physicskit/chaos/tests/test_timeseries.py +131 -0
- physicskit/chaos/tests/test_viewer3d.py +41 -0
- physicskit/chaos/utils/__init__.py +46 -0
- physicskit/chaos/utils/dimension.py +139 -0
- physicskit/chaos/utils/io.py +95 -0
- physicskit/chaos/utils/metrics.py +304 -0
- physicskit/chaos/utils/recurrence.py +122 -0
- physicskit/chaos/utils/spectral.py +69 -0
- physicskit/chaos/utils/timeseries.py +303 -0
- physicskit/chaos/visualizers/__init__.py +85 -0
- physicskit/chaos/visualizers/basins.py +185 -0
- physicskit/chaos/visualizers/bifurcation.py +273 -0
- physicskit/chaos/visualizers/divergence.py +374 -0
- physicskit/chaos/visualizers/dynamic_plots.py +1372 -0
- physicskit/chaos/visualizers/phase_space.py +133 -0
- physicskit/chaos/visualizers/quantum.py +299 -0
- physicskit/chaos/visualizers/recurrence.py +91 -0
- physicskit/chaos/visualizers/section.py +161 -0
- physicskit/chaos/visualizers/theme.py +31 -0
- physicskit/chaos/visualizers/viewer3d.py +82 -0
- physicskit/classical/__init__.py +55 -0
- physicskit/classical/core/__init__.py +17 -0
- physicskit/classical/core/base_system.py +510 -0
- physicskit/classical/core/integrators.py +397 -0
- physicskit/classical/systems/__init__.py +32 -0
- physicskit/classical/systems/chains.py +334 -0
- physicskit/classical/systems/hamiltonian.py +215 -0
- physicskit/classical/systems/lagrangian.py +308 -0
- physicskit/classical/systems/newtonian.py +536 -0
- physicskit/classical/systems/rotations.py +614 -0
- physicskit/classical/tests/__init__.py +0 -0
- physicskit/classical/tests/conftest.py +7 -0
- physicskit/classical/tests/test_base_system.py +113 -0
- physicskit/classical/tests/test_chains.py +84 -0
- physicskit/classical/tests/test_conservation.py +244 -0
- physicskit/classical/tests/test_conservation_utils.py +47 -0
- physicskit/classical/tests/test_hamiltonian.py +44 -0
- physicskit/classical/tests/test_integrators.py +123 -0
- physicskit/classical/tests/test_lagrangian.py +69 -0
- physicskit/classical/tests/test_newtonian.py +45 -0
- physicskit/classical/tests/test_rotations.py +160 -0
- physicskit/classical/tests/test_stepsize.py +149 -0
- physicskit/classical/tests/test_symbolic.py +125 -0
- physicskit/classical/tests/test_visualizers.py +172 -0
- physicskit/classical/utils/__init__.py +20 -0
- physicskit/classical/utils/conservation.py +126 -0
- physicskit/classical/utils/stepsize.py +187 -0
- physicskit/classical/utils/symbolic.py +235 -0
- physicskit/classical/visualizers/__init__.py +25 -0
- physicskit/classical/visualizers/animations.py +508 -0
- physicskit/classical/visualizers/interactive.py +113 -0
- physicskit/classical/visualizers/modal_analysis.py +97 -0
- physicskit/classical/visualizers/phase_space.py +214 -0
- physicskit/condensed/__init__.py +122 -0
- physicskit/condensed/anderson_localization.py +155 -0
- physicskit/condensed/correlated.py +284 -0
- physicskit/condensed/ginzburg_landau.py +220 -0
- physicskit/condensed/landau_levels.py +246 -0
- physicskit/condensed/laughlin.py +229 -0
- physicskit/condensed/models.py +627 -0
- physicskit/condensed/tests/__init__.py +0 -0
- physicskit/condensed/tests/test_condensed.py +518 -0
- physicskit/condensed/tests/test_visualizers.py +87 -0
- physicskit/condensed/tight_binding.py +445 -0
- physicskit/condensed/topological_insulator_3d.py +178 -0
- physicskit/condensed/topology.py +217 -0
- physicskit/condensed/visualizers.py +296 -0
- physicskit/condensed/weyl.py +180 -0
- physicskit/constants.py +184 -0
- physicskit/fields/__init__.py +111 -0
- physicskit/fields/electrodynamics.py +671 -0
- physicskit/fields/quantum_fields.py +510 -0
- physicskit/fields/solitons.py +527 -0
- physicskit/fields/tests/__init__.py +0 -0
- physicskit/fields/tests/conftest.py +7 -0
- physicskit/fields/tests/test_field_animations.py +154 -0
- physicskit/fields/tests/test_fields.py +439 -0
- physicskit/fields/tests/test_visualizers.py +101 -0
- physicskit/fields/visualizers.py +503 -0
- physicskit/fluids/__init__.py +134 -0
- physicskit/fluids/core/__init__.py +23 -0
- physicskit/fluids/core/grid.py +165 -0
- physicskit/fluids/core/timestepping.py +231 -0
- physicskit/fluids/exceptions.py +26 -0
- physicskit/fluids/systems/__init__.py +70 -0
- physicskit/fluids/systems/compressible_flow.py +281 -0
- physicskit/fluids/systems/instabilities.py +343 -0
- physicskit/fluids/systems/navier_stokes.py +221 -0
- physicskit/fluids/systems/potential_flow.py +440 -0
- physicskit/fluids/systems/viscous_flow.py +425 -0
- physicskit/fluids/systems/vortex_dynamics.py +304 -0
- physicskit/fluids/tests/__init__.py +0 -0
- physicskit/fluids/tests/conftest.py +7 -0
- physicskit/fluids/tests/test_compressible_flow.py +66 -0
- physicskit/fluids/tests/test_instabilities.py +115 -0
- physicskit/fluids/tests/test_navier_stokes.py +67 -0
- physicskit/fluids/tests/test_potential_flow.py +92 -0
- physicskit/fluids/tests/test_static_visualizers.py +91 -0
- physicskit/fluids/tests/test_utils.py +100 -0
- physicskit/fluids/tests/test_viscous_flow.py +110 -0
- physicskit/fluids/tests/test_visualizers.py +34 -0
- physicskit/fluids/tests/test_vortex_dynamics.py +88 -0
- physicskit/fluids/utils/__init__.py +18 -0
- physicskit/fluids/utils/dimensionless.py +239 -0
- physicskit/fluids/utils/spectral_analysis.py +142 -0
- physicskit/fluids/visualizers/__init__.py +14 -0
- physicskit/fluids/visualizers/compressible.py +39 -0
- physicskit/fluids/visualizers/flow_fields.py +252 -0
- physicskit/fluids/visualizers/potential_flow.py +51 -0
- physicskit/fluids/visualizers/spectra.py +62 -0
- physicskit/fluids/visualizers/theme.py +38 -0
- physicskit/integrators/__init__.py +52 -0
- physicskit/integrators/adaptive.py +199 -0
- physicskit/integrators/fixed_step.py +360 -0
- physicskit/integrators/tests/__init__.py +0 -0
- physicskit/integrators/tests/test_integrators.py +101 -0
- physicskit/optics/__init__.py +124 -0
- physicskit/optics/_compat.py +5 -0
- physicskit/optics/gaussian.py +382 -0
- physicskit/optics/quantum_optics.py +428 -0
- physicskit/optics/ray.py +469 -0
- physicskit/optics/tests/__init__.py +0 -0
- physicskit/optics/tests/test_diffraction_animation.py +38 -0
- physicskit/optics/tests/test_gaussian.py +170 -0
- physicskit/optics/tests/test_quantum_optics.py +147 -0
- physicskit/optics/tests/test_ray.py +177 -0
- physicskit/optics/tests/test_visualizers.py +58 -0
- physicskit/optics/tests/test_wave.py +146 -0
- physicskit/optics/visualizers.py +264 -0
- physicskit/optics/wave.py +365 -0
- physicskit/particle/__init__.py +152 -0
- physicskit/particle/collider.py +382 -0
- physicskit/particle/confinement.py +125 -0
- physicskit/particle/decays.py +357 -0
- physicskit/particle/electroweak.py +300 -0
- physicskit/particle/kinematics.py +271 -0
- physicskit/particle/neutrinos.py +84 -0
- physicskit/particle/nuclear.py +100 -0
- physicskit/particle/scattering.py +159 -0
- physicskit/particle/tests/__init__.py +0 -0
- physicskit/particle/tests/conftest.py +7 -0
- physicskit/particle/tests/test_collider.py +119 -0
- physicskit/particle/tests/test_confinement.py +42 -0
- physicskit/particle/tests/test_decays.py +112 -0
- physicskit/particle/tests/test_electroweak.py +97 -0
- physicskit/particle/tests/test_kinematics.py +138 -0
- physicskit/particle/tests/test_neutrinos.py +36 -0
- physicskit/particle/tests/test_nuclear.py +45 -0
- physicskit/particle/tests/test_scattering.py +64 -0
- physicskit/particle/tests/test_visualizers.py +214 -0
- physicskit/particle/visualizers/__init__.py +38 -0
- physicskit/particle/visualizers/animations.py +705 -0
- physicskit/particle/visualizers/static.py +101 -0
- physicskit/plasma/__init__.py +176 -0
- physicskit/plasma/acceleration.py +152 -0
- physicskit/plasma/instabilities.py +417 -0
- physicskit/plasma/kinetic.py +588 -0
- physicskit/plasma/mhd.py +463 -0
- physicskit/plasma/single_particle.py +562 -0
- physicskit/plasma/tests/__init__.py +0 -0
- physicskit/plasma/tests/conftest.py +7 -0
- physicskit/plasma/tests/test_new_time_domain_features.py +222 -0
- physicskit/plasma/tests/test_plasma.py +232 -0
- physicskit/plasma/tests/test_visualizers.py +79 -0
- physicskit/plasma/turbulence.py +219 -0
- physicskit/plasma/visualizers.py +868 -0
- physicskit/plasma/waves.py +549 -0
- physicskit/quantum/__init__.py +180 -0
- physicskit/quantum/_compat.py +20 -0
- physicskit/quantum/chapters/__init__.py +0 -0
- physicskit/quantum/chapters/entanglement.py +489 -0
- physicskit/quantum/chapters/harmonic_spin.py +517 -0
- physicskit/quantum/chapters/hydrogen_am.py +292 -0
- physicskit/quantum/chapters/perturbation.py +350 -0
- physicskit/quantum/chapters/potentials.py +997 -0
- physicskit/quantum/chapters/spin.py +306 -0
- physicskit/quantum/chapters/wave_packets.py +541 -0
- physicskit/quantum/core/__init__.py +0 -0
- physicskit/quantum/core/eigensolvers.py +311 -0
- physicskit/quantum/core/operators.py +296 -0
- physicskit/quantum/core/solvers.py +271 -0
- physicskit/quantum/tests/__init__.py +0 -0
- physicskit/quantum/tests/conftest.py +7 -0
- physicskit/quantum/tests/test_animations.py +225 -0
- physicskit/quantum/tests/test_compat.py +43 -0
- physicskit/quantum/tests/test_eigensolvers.py +24 -0
- physicskit/quantum/tests/test_entanglement.py +127 -0
- physicskit/quantum/tests/test_harmonic_spin.py +140 -0
- physicskit/quantum/tests/test_hydrogen_am.py +48 -0
- physicskit/quantum/tests/test_measure.py +110 -0
- physicskit/quantum/tests/test_operators.py +105 -0
- physicskit/quantum/tests/test_perturbation.py +112 -0
- physicskit/quantum/tests/test_physics_checks.py +131 -0
- physicskit/quantum/tests/test_potentials.py +191 -0
- physicskit/quantum/tests/test_solvers.py +70 -0
- physicskit/quantum/tests/test_spin.py +22 -0
- physicskit/quantum/tests/test_unitarity.py +84 -0
- physicskit/quantum/tests/test_wave_packets.py +53 -0
- physicskit/quantum/utils/__init__.py +0 -0
- physicskit/quantum/utils/measure.py +253 -0
- physicskit/quantum/visualizers/__init__.py +0 -0
- physicskit/quantum/visualizers/bloch_sphere.py +205 -0
- physicskit/quantum/visualizers/entanglement.py +58 -0
- physicskit/quantum/visualizers/orbitals.py +187 -0
- physicskit/quantum/visualizers/phase_space.py +179 -0
- physicskit/quantum/visualizers/wavefunctions.py +230 -0
- physicskit/relativity/__init__.py +61 -0
- physicskit/relativity/chapters/__init__.py +33 -0
- physicskit/relativity/chapters/cosmology.py +190 -0
- physicskit/relativity/chapters/gw_merger.py +399 -0
- physicskit/relativity/chapters/kerr.py +371 -0
- physicskit/relativity/chapters/lensing.py +196 -0
- physicskit/relativity/chapters/neutron_star.py +149 -0
- physicskit/relativity/chapters/schwarzschild.py +412 -0
- physicskit/relativity/chapters/timekeeping.py +133 -0
- physicskit/relativity/core/__init__.py +12 -0
- physicskit/relativity/core/geodesics.py +216 -0
- physicskit/relativity/core/kerr_raytracer.py +259 -0
- physicskit/relativity/core/raytracer.py +241 -0
- physicskit/relativity/core/tensors.py +468 -0
- physicskit/relativity/tests/__init__.py +0 -0
- physicskit/relativity/tests/test_conservations.py +54 -0
- physicskit/relativity/tests/test_constants.py +34 -0
- physicskit/relativity/tests/test_cosmology.py +61 -0
- physicskit/relativity/tests/test_gw_merger.py +117 -0
- physicskit/relativity/tests/test_kerr.py +116 -0
- physicskit/relativity/tests/test_kerr_raytracer.py +103 -0
- physicskit/relativity/tests/test_lensing.py +75 -0
- physicskit/relativity/tests/test_neutron_star.py +48 -0
- physicskit/relativity/tests/test_raytracer.py +67 -0
- physicskit/relativity/tests/test_schwarzschild.py +126 -0
- physicskit/relativity/tests/test_shadow_render.py +81 -0
- physicskit/relativity/tests/test_tensors.py +86 -0
- physicskit/relativity/tests/test_timekeeping.py +51 -0
- physicskit/relativity/tests/test_visualizers_misc.py +115 -0
- physicskit/relativity/utils/__init__.py +1 -0
- physicskit/relativity/utils/constants.py +232 -0
- physicskit/relativity/visualizers/__init__.py +46 -0
- physicskit/relativity/visualizers/interactive.py +127 -0
- physicskit/relativity/visualizers/shadow_render.py +367 -0
- physicskit/relativity/visualizers/spacetime_3d.py +92 -0
- physicskit/relativity/visualizers/spacetime_diagrams.py +198 -0
- physicskit/relativity/visualizers/wave_plots.py +185 -0
- physicskit/rmt/__init__.py +8 -0
- physicskit/rmt/ensembles/__init__.py +108 -0
- physicskit/rmt/ensembles/banded.py +114 -0
- physicskit/rmt/ensembles/base.py +135 -0
- physicskit/rmt/ensembles/bdg.py +218 -0
- physicskit/rmt/ensembles/chiral.py +308 -0
- physicskit/rmt/ensembles/circular.py +104 -0
- physicskit/rmt/ensembles/crossover.py +82 -0
- physicskit/rmt/ensembles/density_matrix.py +148 -0
- physicskit/rmt/ensembles/effective_hamiltonian.py +230 -0
- physicskit/rmt/ensembles/embedded.py +207 -0
- physicskit/rmt/ensembles/gaussian.py +153 -0
- physicskit/rmt/ensembles/ginibre.py +110 -0
- physicskit/rmt/ensembles/girko.py +190 -0
- physicskit/rmt/ensembles/graph_laplacian.py +92 -0
- physicskit/rmt/ensembles/haar.py +57 -0
- physicskit/rmt/ensembles/jacobi.py +182 -0
- physicskit/rmt/ensembles/poisson.py +77 -0
- physicskit/rmt/ensembles/polynomial.py +139 -0
- physicskit/rmt/ensembles/pt_symmetric.py +120 -0
- physicskit/rmt/ensembles/single_ring.py +170 -0
- physicskit/rmt/ensembles/sparse.py +95 -0
- physicskit/rmt/ensembles/syk.py +170 -0
- physicskit/rmt/ensembles/truncated_unitary.py +80 -0
- physicskit/rmt/ensembles/universality.py +92 -0
- physicskit/rmt/ensembles/wishart.py +88 -0
- physicskit/rmt/py.typed +0 -0
- physicskit/rmt/spectrum.py +82 -0
- physicskit/rmt/stats/__init__.py +160 -0
- physicskit/rmt/stats/characteristic_polynomial.py +77 -0
- physicskit/rmt/stats/circular_law.py +54 -0
- physicskit/rmt/stats/correlations.py +162 -0
- physicskit/rmt/stats/density.py +70 -0
- physicskit/rmt/stats/entanglement.py +36 -0
- physicskit/rmt/stats/free_probability.py +202 -0
- physicskit/rmt/stats/localization.py +338 -0
- physicskit/rmt/stats/marchenko_pastur.py +55 -0
- physicskit/rmt/stats/pt_symmetric.py +265 -0
- physicskit/rmt/stats/ratios.py +110 -0
- physicskit/rmt/stats/real_ginibre.py +134 -0
- physicskit/rmt/stats/rigidity.py +272 -0
- physicskit/rmt/stats/single_ring.py +95 -0
- physicskit/rmt/stats/spacing.py +150 -0
- physicskit/rmt/stats/tracy_widom.py +175 -0
- physicskit/rmt/stats/truncated_unitary.py +40 -0
- physicskit/rmt/stats/unfolding.py +37 -0
- physicskit/rmt/stats/wachter.py +67 -0
- physicskit/rmt/tests/__init__.py +0 -0
- physicskit/rmt/tests/cache_utils.py +107 -0
- physicskit/rmt/tests/conftest.py +8 -0
- physicskit/rmt/tests/test_banded.py +86 -0
- physicskit/rmt/tests/test_base.py +29 -0
- physicskit/rmt/tests/test_bdg.py +133 -0
- physicskit/rmt/tests/test_characteristic_polynomial.py +50 -0
- physicskit/rmt/tests/test_chiral.py +273 -0
- physicskit/rmt/tests/test_circular.py +225 -0
- physicskit/rmt/tests/test_crossover.py +72 -0
- physicskit/rmt/tests/test_density.py +23 -0
- physicskit/rmt/tests/test_density_matrix.py +181 -0
- physicskit/rmt/tests/test_effective_hamiltonian.py +140 -0
- physicskit/rmt/tests/test_embedded.py +169 -0
- physicskit/rmt/tests/test_free_probability.py +81 -0
- physicskit/rmt/tests/test_gaussian_ensembles.py +22 -0
- physicskit/rmt/tests/test_gaussian_semicircle.py +127 -0
- physicskit/rmt/tests/test_ginibre_circular_law.py +131 -0
- physicskit/rmt/tests/test_girko.py +178 -0
- physicskit/rmt/tests/test_graph_laplacian.py +102 -0
- physicskit/rmt/tests/test_haar.py +118 -0
- physicskit/rmt/tests/test_jacobi_wachter.py +136 -0
- physicskit/rmt/tests/test_localization.py +240 -0
- physicskit/rmt/tests/test_poisson.py +149 -0
- physicskit/rmt/tests/test_polynomial.py +107 -0
- physicskit/rmt/tests/test_pt_symmetric.py +178 -0
- physicskit/rmt/tests/test_random_state.py +19 -0
- physicskit/rmt/tests/test_real_ginibre.py +112 -0
- physicskit/rmt/tests/test_rigidity_universality.py +167 -0
- physicskit/rmt/tests/test_single_ring.py +132 -0
- physicskit/rmt/tests/test_spacing_and_ratios.py +122 -0
- physicskit/rmt/tests/test_sparse.py +71 -0
- physicskit/rmt/tests/test_spectrum.py +27 -0
- physicskit/rmt/tests/test_syk.py +136 -0
- physicskit/rmt/tests/test_tracy_widom.py +113 -0
- physicskit/rmt/tests/test_tridiagonal.py +41 -0
- physicskit/rmt/tests/test_truncated_unitary.py +80 -0
- physicskit/rmt/tests/test_validation_base.py +25 -0
- physicskit/rmt/tests/test_validation_misc.py +67 -0
- physicskit/rmt/tests/test_wishart_marchenko_pastur.py +120 -0
- physicskit/rmt/utils/__init__.py +0 -0
- physicskit/rmt/utils/haar.py +128 -0
- physicskit/rmt/utils/random_state.py +24 -0
- physicskit/rmt/utils/tridiagonal.py +167 -0
- physicskit/rmt/validation/__init__.py +36 -0
- physicskit/rmt/validation/base.py +126 -0
- physicskit/rmt/validation/circular_law.py +80 -0
- physicskit/rmt/validation/marchenko_pastur.py +53 -0
- physicskit/rmt/validation/ratio_distribution.py +53 -0
- physicskit/rmt/validation/real_ginibre.py +60 -0
- physicskit/rmt/validation/sine_kernel.py +57 -0
- physicskit/rmt/validation/single_ring.py +68 -0
- physicskit/rmt/validation/tracy_widom.py +89 -0
- physicskit/rmt/validation/universality.py +100 -0
- physicskit/rmt/validation/wachter.py +52 -0
- physicskit/rmt/validation/wigner_semicircle.py +40 -0
- physicskit/rmt/validation/wigner_surmise.py +58 -0
- physicskit/semiclassical/__init__.py +94 -0
- physicskit/semiclassical/_compat.py +5 -0
- physicskit/semiclassical/core/__init__.py +41 -0
- physicskit/semiclassical/core/gutzwiller.py +215 -0
- physicskit/semiclassical/core/path_integral.py +594 -0
- physicskit/semiclassical/core/propagators.py +579 -0
- physicskit/semiclassical/core/wkb.py +308 -0
- physicskit/semiclassical/systems/__init__.py +13 -0
- physicskit/semiclassical/systems/scarring.py +278 -0
- physicskit/semiclassical/tests/__init__.py +0 -0
- physicskit/semiclassical/tests/test_gutzwiller.py +35 -0
- physicskit/semiclassical/tests/test_path_integral.py +194 -0
- physicskit/semiclassical/tests/test_propagators.py +82 -0
- physicskit/semiclassical/tests/test_scarring.py +43 -0
- physicskit/semiclassical/tests/test_visualizers.py +89 -0
- physicskit/semiclassical/tests/test_wkb.py +30 -0
- physicskit/semiclassical/visualizers/__init__.py +17 -0
- physicskit/semiclassical/visualizers/gutzwiller.py +55 -0
- physicskit/semiclassical/visualizers/path_integral.py +195 -0
- physicskit/semiclassical/visualizers/propagators.py +89 -0
- physicskit/semiclassical/visualizers/scarring.py +146 -0
- physicskit/semiclassical/visualizers/wkb.py +55 -0
- physicskit/statphys/__init__.py +80 -0
- physicskit/statphys/chapters/__init__.py +43 -0
- physicskit/statphys/chapters/ehrenfest_urn.py +112 -0
- physicskit/statphys/chapters/ising_lattice.py +431 -0
- physicskit/statphys/chapters/kpz_growth.py +129 -0
- physicskit/statphys/chapters/molecular_dynamics.py +258 -0
- physicskit/statphys/chapters/nonequilibrium_work.py +146 -0
- physicskit/statphys/chapters/percolation.py +305 -0
- physicskit/statphys/chapters/random_walk.py +142 -0
- physicskit/statphys/chapters/renormalization.py +148 -0
- physicskit/statphys/chapters/sandpile.py +109 -0
- physicskit/statphys/chapters/spin_glass.py +275 -0
- physicskit/statphys/core/__init__.py +10 -0
- physicskit/statphys/core/kpz_engine.py +53 -0
- physicskit/statphys/core/md_engine.py +157 -0
- physicskit/statphys/core/monte_carlo.py +462 -0
- physicskit/statphys/core/random_walk_engine.py +68 -0
- physicskit/statphys/core/sandpile_engine.py +52 -0
- physicskit/statphys/tests/__init__.py +0 -0
- physicskit/statphys/tests/test_dynamics.py +66 -0
- physicskit/statphys/tests/test_ehrenfest_urn.py +44 -0
- physicskit/statphys/tests/test_finite_size_scaling.py +62 -0
- physicskit/statphys/tests/test_interactive.py +33 -0
- physicskit/statphys/tests/test_ising.py +68 -0
- physicskit/statphys/tests/test_kpz.py +55 -0
- physicskit/statphys/tests/test_landau_theory.py +49 -0
- physicskit/statphys/tests/test_md_engine.py +31 -0
- physicskit/statphys/tests/test_molecular_dynamics.py +72 -0
- physicskit/statphys/tests/test_nonequilibrium_work.py +33 -0
- physicskit/statphys/tests/test_partition_function.py +26 -0
- physicskit/statphys/tests/test_percolation.py +116 -0
- physicskit/statphys/tests/test_potts_model.py +46 -0
- physicskit/statphys/tests/test_random_walk.py +50 -0
- physicskit/statphys/tests/test_renormalization.py +44 -0
- physicskit/statphys/tests/test_sandpile.py +44 -0
- physicskit/statphys/tests/test_spin_glass.py +73 -0
- physicskit/statphys/tests/test_thermodynamics.py +121 -0
- physicskit/statphys/tests/test_xy_model.py +45 -0
- physicskit/statphys/utils/__init__.py +1 -0
- physicskit/statphys/utils/dynamics.py +100 -0
- physicskit/statphys/utils/finite_size_scaling.py +171 -0
- physicskit/statphys/utils/landau_theory.py +151 -0
- physicskit/statphys/utils/partition_function.py +109 -0
- physicskit/statphys/utils/thermodynamics.py +356 -0
- physicskit/statphys/visualizers/__init__.py +87 -0
- physicskit/statphys/visualizers/interactive.py +161 -0
- physicskit/statphys/visualizers/jarzynski_render.py +44 -0
- physicskit/statphys/visualizers/kpz_render.py +65 -0
- physicskit/statphys/visualizers/lattice_render.py +212 -0
- physicskit/statphys/visualizers/particle_render.py +121 -0
- physicskit/statphys/visualizers/random_walk_render.py +106 -0
- physicskit/statphys/visualizers/rg_render.py +45 -0
- physicskit/statphys/visualizers/sandpile_render.py +65 -0
- physicskit/statphys/visualizers/spin_glass_render.py +40 -0
- physicskit/statphys/visualizers/urn_render.py +38 -0
- physicskit/statphys/visualizers/vortex_render.py +84 -0
- physicskit/tests/__init__.py +0 -0
- physicskit/tests/test_constants.py +38 -0
- physicskit-0.1.0.dist-info/METADATA +196 -0
- physicskit-0.1.0.dist-info/RECORD +484 -0
- physicskit-0.1.0.dist-info/WHEEL +5 -0
- physicskit-0.1.0.dist-info/licenses/LICENSE +21 -0
- physicskit-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Marchenko-Pastur law benchmark for the Wishart (Laguerre) ensembles.
|
|
2
|
+
|
|
3
|
+
Reference: V. A. Marchenko, L. A. Pastur, Mat. Sb. 72 (1967) 507.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from scipy.interpolate import interp1d
|
|
8
|
+
|
|
9
|
+
from ..stats.marchenko_pastur import mp_pdf, mp_support
|
|
10
|
+
from .base import Benchmark
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MarchenkoPastur(Benchmark):
|
|
14
|
+
"""Validates the empirical spectral density of a Wishart ensemble
|
|
15
|
+
(LOE/LUE/LSE, or general beta-Laguerre) against the exact
|
|
16
|
+
Marchenko-Pastur law for a given aspect ratio gamma = n/m.
|
|
17
|
+
|
|
18
|
+
CDF and its inverse are built once per (gamma, sigma2) via a fine-grid
|
|
19
|
+
cumulative trapezoidal integration and cached as interpolators, for
|
|
20
|
+
the same reason as ``physicskit.rmt.stats.ratios.RatioSurmise``: a KS test
|
|
21
|
+
evaluates the CDF at every data point, so per-point ``quad`` calls
|
|
22
|
+
would be far too slow.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, gamma: float, sigma2: float = 1.0, grid_size: int = 4000) -> None:
|
|
26
|
+
if not (0 < gamma <= 1):
|
|
27
|
+
raise ValueError("gamma must be in (0, 1] for this ensemble convention (m >= n samples/variables, no point mass at zero)")
|
|
28
|
+
self.gamma = gamma
|
|
29
|
+
self.sigma2 = sigma2
|
|
30
|
+
self._lo, self._hi = mp_support(gamma, sigma2)
|
|
31
|
+
|
|
32
|
+
from scipy.integrate import cumulative_trapezoid
|
|
33
|
+
|
|
34
|
+
grid = np.linspace(self._lo, self._hi, grid_size)
|
|
35
|
+
pdf_vals = mp_pdf(grid, gamma, sigma2)
|
|
36
|
+
cdf_vals = cumulative_trapezoid(pdf_vals, grid, initial=0.0)
|
|
37
|
+
cdf_vals /= cdf_vals[-1] # correct for edge-integrable-singularity discretization error
|
|
38
|
+
self._grid = grid
|
|
39
|
+
self._pdf_vals = pdf_vals
|
|
40
|
+
self._cdf_interp = interp1d(grid, cdf_vals, bounds_error=False, fill_value=(0.0, 1.0))
|
|
41
|
+
self._inv_cdf_interp = interp1d(cdf_vals, grid, bounds_error=False, fill_value=(self._lo, self._hi))
|
|
42
|
+
|
|
43
|
+
def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
|
|
44
|
+
return mp_pdf(x, self.gamma, self.sigma2)
|
|
45
|
+
|
|
46
|
+
def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
|
|
47
|
+
x = np.asarray(x, dtype=float)
|
|
48
|
+
out = np.where(x <= self._lo, 0.0, np.where(x >= self._hi, 1.0, self._cdf_interp(x)))
|
|
49
|
+
return out
|
|
50
|
+
|
|
51
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
52
|
+
u = rng.uniform(0.0, 1.0, size=size)
|
|
53
|
+
return self._inv_cdf_interp(u)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Benchmark for the consecutive-spacing ratio distribution.
|
|
2
|
+
|
|
3
|
+
Reference: Y. Y. Atas, E. Bogomolny, O. Giraud, G. Roux, "Distribution of
|
|
4
|
+
the Ratio of Consecutive Level Spacings in Random Matrix Ensembles",
|
|
5
|
+
Phys. Rev. Lett. 110, 084101 (2013).
|
|
6
|
+
|
|
7
|
+
Deliberately structured as an independent check from ``WignerSurmise``:
|
|
8
|
+
the ratio statistic needs no unfolding, so agreement here rules out
|
|
9
|
+
unfolding artifacts as the explanation for agreement with the
|
|
10
|
+
(unfolding-dependent) spacing distribution.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from scipy.stats import kstest, wasserstein_distance
|
|
17
|
+
|
|
18
|
+
from ..stats.ratios import RatioSurmise as _RatioSurmiseModel
|
|
19
|
+
from .base import ValidationResult
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RatioDistribution:
|
|
23
|
+
"""Validates a ratio-statistic sample against the Atas et al. (2013)
|
|
24
|
+
surmise at a given Dyson index beta."""
|
|
25
|
+
|
|
26
|
+
reference_size: int = 200_000
|
|
27
|
+
|
|
28
|
+
def __init__(self, beta: float) -> None:
|
|
29
|
+
self.beta = beta
|
|
30
|
+
self._model = _RatioSurmiseModel(beta)
|
|
31
|
+
|
|
32
|
+
def theoretical_pdf(self, r: np.ndarray) -> np.ndarray:
|
|
33
|
+
return self._model.pdf(r)
|
|
34
|
+
|
|
35
|
+
def theoretical_cdf(self, r: np.ndarray) -> np.ndarray:
|
|
36
|
+
return self._model.cdf(r)
|
|
37
|
+
|
|
38
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
39
|
+
return self._model.rvs(size, rng)
|
|
40
|
+
|
|
41
|
+
def validate(self, ratios: np.ndarray, seed: int | np.random.Generator | None = None) -> ValidationResult:
|
|
42
|
+
"""Compare an array of ratio statistics (each in [0, 1], from
|
|
43
|
+
``physicskit.rmt.stats.ratios.ratio_statistics``) against the surmise."""
|
|
44
|
+
ks = kstest(ratios, self.theoretical_cdf)
|
|
45
|
+
rng = np.random.default_rng(seed)
|
|
46
|
+
reference = self.reference_samples(self.reference_size, rng)
|
|
47
|
+
wd = wasserstein_distance(ratios, reference)
|
|
48
|
+
return ValidationResult(
|
|
49
|
+
ks_statistic=float(ks.statistic),
|
|
50
|
+
ks_pvalue=float(ks.pvalue),
|
|
51
|
+
wasserstein_distance=float(wd),
|
|
52
|
+
n_eigenvalues=len(ratios),
|
|
53
|
+
)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Real-Ginibre real-eigenvalue-count benchmark.
|
|
2
|
+
|
|
3
|
+
Like ``physicskit.rmt.validation.sine_kernel.SineKernel``, this doesn't fit the
|
|
4
|
+
KS/Wasserstein ``Benchmark`` interface: it compares a scalar expected
|
|
5
|
+
COUNT (of real eigenvalues), not a sample against a 1-D distribution. It
|
|
6
|
+
gets its own lightweight result type instead.
|
|
7
|
+
|
|
8
|
+
Reference: A. Edelman, E. Kostlan, S. Shub, J. Amer. Math. Soc. 7 (1994)
|
|
9
|
+
247. See ``physicskit.rmt.stats.real_ginibre`` for the precision caveat: the
|
|
10
|
+
theoretical value used here is the verified large-n asymptotic
|
|
11
|
+
expansion, not the exact finite-n Edelman-Kostlan-Shub closed form.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
from ..spectrum import Spectrum
|
|
17
|
+
from ..stats.real_ginibre import real_eigenvalue_count_asymptotic, real_eigenvalue_count_empirical
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class RealEigenvalueCountResult:
|
|
22
|
+
"""Result of comparing a GinOE Spectrum's empirical mean real-
|
|
23
|
+
eigenvalue count against the asymptotic theoretical value."""
|
|
24
|
+
|
|
25
|
+
empirical_mean: float
|
|
26
|
+
theoretical: float
|
|
27
|
+
n: int
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def relative_error(self) -> float:
|
|
31
|
+
return abs(self.empirical_mean - self.theoretical) / self.theoretical
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> str:
|
|
34
|
+
return (
|
|
35
|
+
f"RealEigenvalueCountResult(empirical_mean={self.empirical_mean:.4f}, "
|
|
36
|
+
f"theoretical={self.theoretical:.4f}, n={self.n}, "
|
|
37
|
+
f"relative_error={self.relative_error:.4f})"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RealGinibreEigenvalueCount:
|
|
42
|
+
"""Validates a real-Ginibre (``GinOE``) Spectrum's mean number of
|
|
43
|
+
real eigenvalues per sample against the asymptotic expected count
|
|
44
|
+
sqrt(2n/pi) + 1/2 (see ``physicskit.rmt.stats.real_ginibre`` for the
|
|
45
|
+
asymptotic-vs-exact precision caveat)."""
|
|
46
|
+
|
|
47
|
+
def __init__(self, tol: float = 1e-8) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Parameters
|
|
50
|
+
----------
|
|
51
|
+
tol : float, optional
|
|
52
|
+
Passed to ``real_eigenvalue_count_empirical``: an eigenvalue
|
|
53
|
+
counts as real if ``abs(Im(lambda))`` is below this.
|
|
54
|
+
"""
|
|
55
|
+
self.tol = tol
|
|
56
|
+
|
|
57
|
+
def validate(self, spectrum: Spectrum) -> RealEigenvalueCountResult:
|
|
58
|
+
counts = real_eigenvalue_count_empirical(spectrum.eigenvalues, tol=self.tol)
|
|
59
|
+
theoretical = real_eigenvalue_count_asymptotic(spectrum.n)
|
|
60
|
+
return RealEigenvalueCountResult(empirical_mean=float(counts.mean()), theoretical=theoretical, n=spectrum.n)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Sine-kernel benchmark for the Circular Unitary Ensemble's bulk
|
|
2
|
+
two-point correlation function.
|
|
3
|
+
|
|
4
|
+
This does not fit the KS/Wasserstein ``Benchmark`` interface used
|
|
5
|
+
elsewhere in the package: it validates a *correlation function* (a
|
|
6
|
+
density estimated by pair-counting), not a sample from a 1-D
|
|
7
|
+
distribution. It gets its own lightweight result type instead.
|
|
8
|
+
|
|
9
|
+
Reference: F. J. Dyson, J. Math. Phys. 3 (1962) 140, 157, 166.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from ..spectrum import Spectrum
|
|
17
|
+
from ..stats.correlations import pair_correlation_estimate, sine_kernel_r2
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class CorrelationValidationResult:
|
|
22
|
+
rmse: float
|
|
23
|
+
r_max: float
|
|
24
|
+
n_bins: int
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
return f"CorrelationValidationResult(rmse={self.rmse:.5f}, r_max={self.r_max}, n_bins={self.n_bins})"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class SineKernel:
|
|
31
|
+
"""Validates a CUE (beta=2) Spectrum's bulk two-point correlation
|
|
32
|
+
function against the exact determinantal sine-kernel result.
|
|
33
|
+
|
|
34
|
+
Only beta=2 is implemented -- see module docstring in
|
|
35
|
+
``physicskit.rmt.stats.correlations`` for why the beta=1, 4 cases (COE, CSE)
|
|
36
|
+
are deferred rather than guessed at.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, r_max: float = 4.0, n_bins: int = 60) -> None:
|
|
40
|
+
self.r_max = r_max
|
|
41
|
+
self.n_bins = n_bins
|
|
42
|
+
|
|
43
|
+
def theoretical_r2(self, r: np.ndarray) -> np.ndarray:
|
|
44
|
+
return sine_kernel_r2(r)
|
|
45
|
+
|
|
46
|
+
def validate(self, spectrum: Spectrum) -> CorrelationValidationResult:
|
|
47
|
+
if spectrum.beta != 2:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
"SineKernel currently only validates beta=2 (CUE) spectra; "
|
|
50
|
+
"the beta=1 (COE) and beta=4 (CSE) correlation kernels "
|
|
51
|
+
"require the fuller Pfaffian point-process machinery, "
|
|
52
|
+
"not yet implemented (see physicskit.rmt.stats.correlations)."
|
|
53
|
+
)
|
|
54
|
+
centers, estimate = pair_correlation_estimate(spectrum, r_max=self.r_max, n_bins=self.n_bins)
|
|
55
|
+
theory = self.theoretical_r2(centers)
|
|
56
|
+
rmse = float(np.sqrt(np.mean((estimate - theory) ** 2)))
|
|
57
|
+
return CorrelationValidationResult(rmse=rmse, r_max=self.r_max, n_bins=self.n_bins)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Single ring theorem benchmark for bi-unitarily-invariant non-Hermitian
|
|
2
|
+
ensembles.
|
|
3
|
+
|
|
4
|
+
References: Feinberg-Zee (1997); Guionnet-Krishnapur-Zeitouni (2011) --
|
|
5
|
+
see ``physicskit.rmt.ensembles.single_ring`` for the full construction and
|
|
6
|
+
precision notes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from ..spectrum import Spectrum
|
|
14
|
+
from ..stats.single_ring import annulus_radial_cdf, annulus_radial_pdf
|
|
15
|
+
from .base import Benchmark, ValidationResult
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SingleRingTheorem(Benchmark):
|
|
19
|
+
"""Validates a bi-unitarily-invariant non-Hermitian Spectrum's
|
|
20
|
+
eigenvalue *radii* against the single ring theorem's radial marginal
|
|
21
|
+
(uniform on the annulus ``r_in <= |z| <= r_out``).
|
|
22
|
+
|
|
23
|
+
Like ``CircularLaw`` (its r_in=0 special case), overrides
|
|
24
|
+
``validate`` because the base class compares ``spectrum.rescaled``
|
|
25
|
+
directly, but here it's the eigenvalue *magnitudes* that are
|
|
26
|
+
compared to a 1-D theoretical distribution.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
r_in : float
|
|
31
|
+
r_out : float
|
|
32
|
+
The theoretical ring radii (see
|
|
33
|
+
``physicskit.rmt.stats.single_ring.single_ring_radii`` /
|
|
34
|
+
``single_ring_radii_wishart_theory``).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, r_in: float, r_out: float) -> None:
|
|
38
|
+
if not 0.0 <= r_in < r_out:
|
|
39
|
+
raise ValueError(f"require 0 <= r_in < r_out, got r_in={r_in}, r_out={r_out}")
|
|
40
|
+
self.r_in = r_in
|
|
41
|
+
self.r_out = r_out
|
|
42
|
+
|
|
43
|
+
def theoretical_pdf(self, r: np.ndarray) -> np.ndarray:
|
|
44
|
+
return annulus_radial_pdf(r, self.r_in, self.r_out)
|
|
45
|
+
|
|
46
|
+
def theoretical_cdf(self, r: np.ndarray) -> np.ndarray:
|
|
47
|
+
return annulus_radial_cdf(r, self.r_in, self.r_out)
|
|
48
|
+
|
|
49
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
50
|
+
# Inverse-CDF sampling: F(r) = (r^2-r_in^2)/(r_out^2-r_in^2)
|
|
51
|
+
# => r = sqrt(r_in^2 + U*(r_out^2-r_in^2)), U ~ Uniform(0,1)
|
|
52
|
+
u = rng.uniform(0.0, 1.0, size=size)
|
|
53
|
+
return np.sqrt(self.r_in**2 + u * (self.r_out**2 - self.r_in**2))
|
|
54
|
+
|
|
55
|
+
def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
|
|
56
|
+
from scipy.stats import kstest, wasserstein_distance
|
|
57
|
+
|
|
58
|
+
radii = np.abs(spectrum.rescaled.ravel())
|
|
59
|
+
ks = kstest(radii, self.theoretical_cdf)
|
|
60
|
+
rng = np.random.default_rng(seed)
|
|
61
|
+
reference = self.reference_samples(self.reference_size, rng)
|
|
62
|
+
wd = wasserstein_distance(radii, reference)
|
|
63
|
+
return ValidationResult(
|
|
64
|
+
ks_statistic=float(ks.statistic),
|
|
65
|
+
ks_pvalue=float(ks.pvalue),
|
|
66
|
+
wasserstein_distance=float(wd),
|
|
67
|
+
n_eigenvalues=len(radii),
|
|
68
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Tracy-Widom soft-edge benchmark for the Gaussian ensembles.
|
|
2
|
+
|
|
3
|
+
References: C. A. Tracy, H. Widom, Commun. Math. Phys. 159 (1994) 151;
|
|
4
|
+
Commun. Math. Phys. 177 (1996) 727.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from collections.abc import Callable
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from scipy.stats import kstest, wasserstein_distance
|
|
13
|
+
|
|
14
|
+
from ..ensembles.base import MatrixEnsemble
|
|
15
|
+
from ..spectrum import Spectrum
|
|
16
|
+
from ..stats.tracy_widom import (
|
|
17
|
+
largest_eigenvalues,
|
|
18
|
+
tracy_widom_cdf,
|
|
19
|
+
tracy_widom_edge_scale,
|
|
20
|
+
tracy_widom_rvs,
|
|
21
|
+
)
|
|
22
|
+
from .base import ValidationResult
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TracyWidom:
|
|
26
|
+
"""Validates the largest-eigenvalue statistic of a Gaussian-ensemble
|
|
27
|
+
Spectrum against the Tracy-Widom soft-edge law at the matching beta.
|
|
28
|
+
|
|
29
|
+
Only beta in {1, 2, 4} are supported (F_1, F_2, F_4 have closed
|
|
30
|
+
forms; general beta does not -- see ``physicskit.rmt.stats.tracy_widom``).
|
|
31
|
+
Overrides ``validate`` (rather than just providing
|
|
32
|
+
``theoretical_cdf``/``reference_samples``) because the relevant
|
|
33
|
+
statistic is the *largest* eigenvalue after a beta-dependent edge
|
|
34
|
+
rescaling -- not the full ``spectrum.rescaled`` array the base class
|
|
35
|
+
compares directly.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
reference_size: int = 200_000
|
|
39
|
+
|
|
40
|
+
def __init__(self, beta: int) -> None:
|
|
41
|
+
if beta not in (1, 2, 4):
|
|
42
|
+
raise ValueError(f"TracyWidom only supports beta in (1, 2, 4), got {beta}")
|
|
43
|
+
self.beta = beta
|
|
44
|
+
|
|
45
|
+
def theoretical_cdf(self, s: np.ndarray) -> np.ndarray:
|
|
46
|
+
return tracy_widom_cdf(s, self.beta)
|
|
47
|
+
|
|
48
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
49
|
+
return tracy_widom_rvs(size, self.beta, rng)
|
|
50
|
+
|
|
51
|
+
def validate(self, spectrum: Spectrum, seed: int | np.random.Generator | None = None) -> ValidationResult:
|
|
52
|
+
if spectrum.beta != self.beta:
|
|
53
|
+
raise ValueError(f"Spectrum beta ({spectrum.beta}) does not match TracyWidom benchmark beta ({self.beta})")
|
|
54
|
+
lam_max = largest_eigenvalues(spectrum)
|
|
55
|
+
scale = tracy_widom_edge_scale(spectrum.n, self.beta)
|
|
56
|
+
edge_stat = scale * (lam_max - 2.0)
|
|
57
|
+
|
|
58
|
+
ks = kstest(edge_stat, self.theoretical_cdf)
|
|
59
|
+
rng = np.random.default_rng(seed)
|
|
60
|
+
reference = self.reference_samples(self.reference_size, rng)
|
|
61
|
+
wd = wasserstein_distance(edge_stat, reference)
|
|
62
|
+
return ValidationResult(
|
|
63
|
+
ks_statistic=float(ks.statistic),
|
|
64
|
+
ks_pvalue=float(ks.pvalue),
|
|
65
|
+
wasserstein_distance=float(wd),
|
|
66
|
+
n_eigenvalues=len(edge_stat),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def convergence_curve(
|
|
70
|
+
self,
|
|
71
|
+
ensemble_factory: Callable[..., MatrixEnsemble],
|
|
72
|
+
n_values: list[int],
|
|
73
|
+
n_samples: int = 200,
|
|
74
|
+
seed: int | np.random.Generator | None = None,
|
|
75
|
+
) -> list[tuple[int, ValidationResult]]:
|
|
76
|
+
"""Convergence to Tracy-Widom is notoriously slow (the largest
|
|
77
|
+
eigenvalue is a single extreme-value statistic per sample, so
|
|
78
|
+
each matrix contributes only one data point -- n_samples needs
|
|
79
|
+
to be much larger than in the bulk-statistic benchmarks to get
|
|
80
|
+
a stable KS estimate at all). Expect a shallower, noisier trend
|
|
81
|
+
than the semicircle/Marchenko-Pastur convergence curves."""
|
|
82
|
+
rng = np.random.default_rng(seed)
|
|
83
|
+
results = []
|
|
84
|
+
for n in n_values:
|
|
85
|
+
ensemble = ensemble_factory(n, seed=int(rng.integers(1 << 31)))
|
|
86
|
+
spectrum = ensemble.sample(n_samples=n_samples)
|
|
87
|
+
result = self.validate(spectrum, seed=int(rng.integers(1 << 31)))
|
|
88
|
+
results.append((n, result))
|
|
89
|
+
return results
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Universality benchmark: confirms that the semicircle law (and,
|
|
2
|
+
optionally, the Wigner surmise) hold across several different entry
|
|
3
|
+
distributions, not just Gaussian ones -- a stronger validation of "the
|
|
4
|
+
theory is implemented correctly" than fitting a single Gaussian case
|
|
5
|
+
ever could be, since it tests the actual universality *claim* of RMT
|
|
6
|
+
rather than one specific realization of it.
|
|
7
|
+
|
|
8
|
+
This module ties together ``physicskit.rmt.ensembles.universality`` (the
|
|
9
|
+
entry-distribution-agnostic ensemble) and the already-existing
|
|
10
|
+
``WignerSemicircle`` / ``WignerSurmise`` benchmarks -- no new
|
|
11
|
+
theoretical curve is needed here, since universality means the SAME
|
|
12
|
+
curve applies regardless of entry distribution.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
from ..ensembles.universality import (
|
|
23
|
+
GeneralWignerEnsemble,
|
|
24
|
+
exponential_centered_unit_variance,
|
|
25
|
+
rademacher,
|
|
26
|
+
uniform_unit_variance,
|
|
27
|
+
)
|
|
28
|
+
from .base import ValidationResult
|
|
29
|
+
from .wigner_semicircle import WignerSemicircle
|
|
30
|
+
|
|
31
|
+
EntrySampler = Callable[[np.random.Generator, tuple[int, ...]], np.ndarray]
|
|
32
|
+
|
|
33
|
+
#: A representative, deliberately varied set of entry distributions:
|
|
34
|
+
#: symmetric-bounded (uniform), symmetric-discrete (Rademacher), and
|
|
35
|
+
#: asymmetric-unbounded (centered exponential) -- if universality holds
|
|
36
|
+
#: across all three, entry-distribution shape/boundedness/symmetry are
|
|
37
|
+
#: all ruled out as confounds.
|
|
38
|
+
DEFAULT_ENTRY_DISTRIBUTIONS: dict[str, EntrySampler] = {
|
|
39
|
+
"uniform": uniform_unit_variance,
|
|
40
|
+
"rademacher": rademacher,
|
|
41
|
+
"exponential": exponential_centered_unit_variance,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class UniversalityResult:
|
|
47
|
+
"""Per-distribution validation results plus a summary judgment."""
|
|
48
|
+
|
|
49
|
+
per_distribution: dict[str, ValidationResult] = field(default_factory=dict)
|
|
50
|
+
max_ks_statistic: float = 0.0
|
|
51
|
+
|
|
52
|
+
def __repr__(self):
|
|
53
|
+
lines = [f" {name}: {result}" for name, result in self.per_distribution.items()]
|
|
54
|
+
return "UniversalityResult(\n" + "\n".join(lines) + f"\n max_ks={self.max_ks_statistic:.5f}\n)"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def check_universality(
|
|
58
|
+
n: int,
|
|
59
|
+
beta: int,
|
|
60
|
+
n_samples: int = 25,
|
|
61
|
+
seed: int | None = None,
|
|
62
|
+
entry_distributions: dict[str, EntrySampler] | None = None,
|
|
63
|
+
ks_threshold: float = 0.02,
|
|
64
|
+
) -> UniversalityResult:
|
|
65
|
+
"""Sample ``GeneralWignerEnsemble`` with several entry distributions
|
|
66
|
+
at the given n and beta, validate each against the Wigner semicircle
|
|
67
|
+
law, and return per-distribution results plus a pass/fail summary.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
n : int
|
|
72
|
+
beta : int
|
|
73
|
+
1 or 2 (see ``GeneralWignerEnsemble``).
|
|
74
|
+
n_samples : int
|
|
75
|
+
seed : int or None
|
|
76
|
+
entry_distributions : dict or None
|
|
77
|
+
name -> entry_sampler callable; defaults to
|
|
78
|
+
``DEFAULT_ENTRY_DISTRIBUTIONS``.
|
|
79
|
+
ks_threshold : float
|
|
80
|
+
Each distribution's KS statistic against the semicircle law
|
|
81
|
+
must be below this for the overall check to pass.
|
|
82
|
+
|
|
83
|
+
Returns
|
|
84
|
+
-------
|
|
85
|
+
UniversalityResult
|
|
86
|
+
"""
|
|
87
|
+
if entry_distributions is None:
|
|
88
|
+
entry_distributions = DEFAULT_ENTRY_DISTRIBUTIONS
|
|
89
|
+
|
|
90
|
+
benchmark = WignerSemicircle()
|
|
91
|
+
per_distribution = {}
|
|
92
|
+
for i, (name, sampler) in enumerate(entry_distributions.items()):
|
|
93
|
+
ensemble_seed = None if seed is None else seed + i
|
|
94
|
+
ensemble = GeneralWignerEnsemble(n=n, entry_sampler=sampler, beta=beta, seed=ensemble_seed)
|
|
95
|
+
spectrum = ensemble.sample(n_samples=n_samples)
|
|
96
|
+
result = benchmark.validate(spectrum, seed=ensemble_seed)
|
|
97
|
+
per_distribution[name] = result
|
|
98
|
+
|
|
99
|
+
max_ks = max(r.ks_statistic for r in per_distribution.values())
|
|
100
|
+
return UniversalityResult(per_distribution=per_distribution, max_ks_statistic=max_ks)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Wachter distribution benchmark for the Jacobi (MANOVA) ensembles.
|
|
2
|
+
|
|
3
|
+
Reference: K. W. Wachter, Ann. Statist. 8 (1980) 937.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from scipy.integrate import cumulative_trapezoid
|
|
8
|
+
from scipy.interpolate import interp1d
|
|
9
|
+
|
|
10
|
+
from ..stats.wachter import wachter_pdf, wachter_support
|
|
11
|
+
from .base import Benchmark
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Wachter(Benchmark):
|
|
15
|
+
"""Validates the empirical spectral density of a Jacobi ensemble
|
|
16
|
+
(JOE/JUE) against the exact Wachter law for given inverse aspect
|
|
17
|
+
ratios a = m1/n, b = m2/n.
|
|
18
|
+
|
|
19
|
+
CDF and its inverse are built once via a fine-grid cumulative
|
|
20
|
+
trapezoidal integration and cached as interpolators, for the same
|
|
21
|
+
reason as ``physicskit.rmt.validation.marchenko_pastur.MarchenkoPastur``: a
|
|
22
|
+
KS test evaluates the CDF at every data point, so per-point
|
|
23
|
+
``quad`` calls (as in ``physicskit.rmt.stats.wachter.wachter_cdf``) would be
|
|
24
|
+
far too slow.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, a: float, b: float, grid_size: int = 4000) -> None:
|
|
28
|
+
if a < 1 or b < 1:
|
|
29
|
+
raise ValueError(f"a and b are inverse aspect ratios (m/n) and must be >= 1; got a={a}, b={b}")
|
|
30
|
+
self.a = a
|
|
31
|
+
self.b = b
|
|
32
|
+
self._lo, self._hi = wachter_support(a, b)
|
|
33
|
+
|
|
34
|
+
grid = np.linspace(self._lo, self._hi, grid_size)
|
|
35
|
+
pdf_vals = wachter_pdf(grid, a, b)
|
|
36
|
+
cdf_vals = cumulative_trapezoid(pdf_vals, grid, initial=0.0)
|
|
37
|
+
cdf_vals /= cdf_vals[-1]
|
|
38
|
+
self._grid = grid
|
|
39
|
+
self._pdf_vals = pdf_vals
|
|
40
|
+
self._cdf_interp = interp1d(grid, cdf_vals, bounds_error=False, fill_value=(0.0, 1.0))
|
|
41
|
+
self._inv_cdf_interp = interp1d(cdf_vals, grid, bounds_error=False, fill_value=(self._lo, self._hi))
|
|
42
|
+
|
|
43
|
+
def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
|
|
44
|
+
return wachter_pdf(x, self.a, self.b)
|
|
45
|
+
|
|
46
|
+
def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
|
|
47
|
+
x = np.asarray(x, dtype=float)
|
|
48
|
+
return np.where(x <= self._lo, 0.0, np.where(x >= self._hi, 1.0, self._cdf_interp(x)))
|
|
49
|
+
|
|
50
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
51
|
+
u = rng.uniform(0.0, 1.0, size=size)
|
|
52
|
+
return self._inv_cdf_interp(u)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Wigner semicircle law benchmark.
|
|
2
|
+
|
|
3
|
+
References
|
|
4
|
+
----------
|
|
5
|
+
E. Wigner, "Characteristic Vectors of Bordered Matrices With Infinite
|
|
6
|
+
Dimensions", Ann. Math. 62 (1955) 548.
|
|
7
|
+
E. Wigner, "On the Distribution of the Roots of Certain Symmetric
|
|
8
|
+
Matrices", Ann. Math. 67 (1958) 325.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
from scipy.stats import beta as beta_dist
|
|
13
|
+
|
|
14
|
+
from ..stats.density import semicircle_cdf, semicircle_pdf
|
|
15
|
+
from .base import Benchmark
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WignerSemicircle(Benchmark):
|
|
19
|
+
"""Validates the empirical spectral density of a Gaussian
|
|
20
|
+
beta-ensemble (GOE/GUE/GSE, or general beta-Hermite) against the
|
|
21
|
+
exact Wigner semicircle law.
|
|
22
|
+
|
|
23
|
+
The semicircle distribution on [-radius, radius] is an affine
|
|
24
|
+
transform of Beta(3/2, 3/2): if U ~ Beta(3/2, 3/2), then
|
|
25
|
+
X = radius * (2U - 1) has semicircle density, which is how
|
|
26
|
+
``reference_samples`` draws from the theoretical law.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self, radius: float = 2.0) -> None:
|
|
30
|
+
self.radius = radius
|
|
31
|
+
|
|
32
|
+
def theoretical_cdf(self, x: np.ndarray) -> np.ndarray:
|
|
33
|
+
return semicircle_cdf(x, radius=self.radius)
|
|
34
|
+
|
|
35
|
+
def theoretical_pdf(self, x: np.ndarray) -> np.ndarray:
|
|
36
|
+
return semicircle_pdf(x, radius=self.radius)
|
|
37
|
+
|
|
38
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
39
|
+
u = beta_dist.rvs(1.5, 1.5, size=size, random_state=rng)
|
|
40
|
+
return self.radius * (2.0 * u - 1.0)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Benchmark for the (generalized) Wigner surmise -- the nearest-neighbor
|
|
2
|
+
spacing distribution of the Gaussian ensembles.
|
|
3
|
+
|
|
4
|
+
Unlike ``WignerSemicircle``, this benchmark validates a *derived*
|
|
5
|
+
statistic (unfolded spacings), so ``validate`` here expects pre-computed
|
|
6
|
+
spacings (from ``physicskit.rmt.stats.spacing.nearest_neighbor_spacings``), not a
|
|
7
|
+
raw Spectrum -- unfolding and edge-trimming choices belong to the caller,
|
|
8
|
+
not baked into the benchmark.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
from scipy.stats import kstest, wasserstein_distance
|
|
15
|
+
|
|
16
|
+
from ..stats.spacing import (
|
|
17
|
+
wigner_surmise_cdf,
|
|
18
|
+
wigner_surmise_pdf,
|
|
19
|
+
wigner_surmise_samples,
|
|
20
|
+
)
|
|
21
|
+
from .base import ValidationResult
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class WignerSurmise:
|
|
25
|
+
"""Validates a spacing sample against the generalized Wigner surmise
|
|
26
|
+
at a given Dyson index beta.
|
|
27
|
+
|
|
28
|
+
References: Wigner's original surmise (beta=1); Mehta, *Random
|
|
29
|
+
Matrices* (3rd ed.), 2004, for the beta=2, 4 cases.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
reference_size: int = 200_000
|
|
33
|
+
|
|
34
|
+
def __init__(self, beta: float) -> None:
|
|
35
|
+
self.beta = beta
|
|
36
|
+
|
|
37
|
+
def theoretical_pdf(self, s: np.ndarray) -> np.ndarray:
|
|
38
|
+
return wigner_surmise_pdf(s, self.beta)
|
|
39
|
+
|
|
40
|
+
def theoretical_cdf(self, s: np.ndarray) -> np.ndarray:
|
|
41
|
+
return wigner_surmise_cdf(s, self.beta)
|
|
42
|
+
|
|
43
|
+
def reference_samples(self, size: int, rng: np.random.Generator) -> np.ndarray:
|
|
44
|
+
return wigner_surmise_samples(size, self.beta, rng)
|
|
45
|
+
|
|
46
|
+
def validate(self, spacings: np.ndarray, seed: int | np.random.Generator | None = None) -> ValidationResult:
|
|
47
|
+
"""Compare an array of (already unfolded, already edge-trimmed)
|
|
48
|
+
spacings against the surmise."""
|
|
49
|
+
ks = kstest(spacings, self.theoretical_cdf)
|
|
50
|
+
rng = np.random.default_rng(seed)
|
|
51
|
+
reference = self.reference_samples(self.reference_size, rng)
|
|
52
|
+
wd = wasserstein_distance(spacings, reference)
|
|
53
|
+
return ValidationResult(
|
|
54
|
+
ks_statistic=float(ks.statistic),
|
|
55
|
+
ks_pvalue=float(ks.pvalue),
|
|
56
|
+
wasserstein_distance=float(wd),
|
|
57
|
+
n_eigenvalues=len(spacings),
|
|
58
|
+
)
|