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,178 @@
|
|
|
1
|
+
"""Tests for PTSymmetricEnsemble and the real-eigenvalue-fraction
|
|
2
|
+
statistic.
|
|
3
|
+
|
|
4
|
+
The pseudo-Hermiticity relation P H P = H^dagger (P the fixed +-1
|
|
5
|
+
signature matrix) was verified numerically to machine precision during
|
|
6
|
+
development for both beta=1 and beta=2 before this construction was
|
|
7
|
+
trusted -- see module docstring in
|
|
8
|
+
``physicskit.rmt/ensembles/pt_symmetric.py``.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
from cache_utils import cached_sample
|
|
14
|
+
|
|
15
|
+
import physicskit.rmt as rmt
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.mark.parametrize("beta", [1, 2])
|
|
19
|
+
def test_pseudo_hermiticity_holds_exactly(beta):
|
|
20
|
+
p, q = 8, 6
|
|
21
|
+
ens = rmt.ensembles.PTSymmetricEnsemble(p=p, q=q, g=0.7, beta=beta, seed=0)
|
|
22
|
+
h = ens._sample_matrix(np.random.default_rng(0))
|
|
23
|
+
signature = np.diag(np.concatenate([np.ones(p), -np.ones(q)]))
|
|
24
|
+
residual = signature @ h @ signature - h.conj().T
|
|
25
|
+
assert np.abs(residual).max() < 1e-10
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize("beta", [1, 2])
|
|
29
|
+
def test_zero_coupling_gives_fully_real_spectrum(beta):
|
|
30
|
+
ens = rmt.ensembles.PTSymmetricEnsemble(p=10, q=10, g=0.0, beta=beta, seed=1)
|
|
31
|
+
spectrum = cached_sample(ens, n_samples=10)
|
|
32
|
+
frac = rmt.stats.real_eigenvalue_fraction(spectrum.eigenvalues)
|
|
33
|
+
np.testing.assert_allclose(frac, 1.0, atol=1e-6)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@pytest.mark.parametrize("beta", [1, 2])
|
|
37
|
+
def test_real_fraction_decreases_as_coupling_grows(beta):
|
|
38
|
+
# The PT-symmetry-breaking transition: increasing coupling g should
|
|
39
|
+
# drive the spectrum from fully real toward mostly complex.
|
|
40
|
+
fracs = []
|
|
41
|
+
for g in [0.0, 0.3, 1.0, 3.0]:
|
|
42
|
+
ens = rmt.ensembles.PTSymmetricEnsemble(p=15, q=15, g=g, beta=beta, seed=2)
|
|
43
|
+
spectrum = cached_sample(ens, n_samples=20)
|
|
44
|
+
fracs.append(rmt.stats.real_eigenvalue_fraction(spectrum.eigenvalues).mean())
|
|
45
|
+
assert fracs == sorted(fracs, reverse=True)
|
|
46
|
+
assert fracs[0] == pytest.approx(1.0, abs=1e-6)
|
|
47
|
+
assert fracs[-1] < 0.3
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_eigenvalues_are_real_or_conjugate_pairs():
|
|
51
|
+
# Pseudo-Hermiticity forces the eigenvalue SET to be closed under
|
|
52
|
+
# complex conjugation: every non-real eigenvalue has a partner at
|
|
53
|
+
# its complex conjugate (matched here via nearest-neighbor distance,
|
|
54
|
+
# not a naive sort, since floating-point noise can break exact ties
|
|
55
|
+
# between a conjugate pair's real parts).
|
|
56
|
+
ens = rmt.ensembles.PTSymmetricEnsemble(p=10, q=10, g=1.0, beta=2, seed=3)
|
|
57
|
+
spectrum = ens.sample(n_samples=1)
|
|
58
|
+
eigs = spectrum.eigenvalues[0]
|
|
59
|
+
non_real = eigs[np.abs(eigs.imag) > 1e-6]
|
|
60
|
+
for lam in non_real:
|
|
61
|
+
nearest_dist = np.min(np.abs(eigs - np.conj(lam)))
|
|
62
|
+
assert nearest_dist < 1e-8
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_invalid_parameters_rejected():
|
|
66
|
+
with pytest.raises(ValueError):
|
|
67
|
+
rmt.ensembles.PTSymmetricEnsemble(p=5, q=5, g=-1.0, seed=0)
|
|
68
|
+
with pytest.raises(ValueError):
|
|
69
|
+
rmt.ensembles.PTSymmetricEnsemble(p=5, q=5, g=1.0, beta=4, seed=0)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_reproducibility():
|
|
73
|
+
ens_a = rmt.ensembles.PTSymmetricEnsemble(p=6, q=6, g=0.5, seed=42)
|
|
74
|
+
ens_b = rmt.ensembles.PTSymmetricEnsemble(p=6, q=6, g=0.5, seed=42)
|
|
75
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
76
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
77
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# --- exceptional-point detection ---
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_find_exceptional_points_finds_real_count_drops():
|
|
84
|
+
eps = rmt.stats.find_exceptional_points(p=6, q=6, beta=2, seed=0, g_max=3.0, n_grid=3000)
|
|
85
|
+
assert len(eps) > 0
|
|
86
|
+
assert np.all(eps > 0.0)
|
|
87
|
+
assert np.all(eps < 3.0)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_eigenvector_condition_number_spikes_near_exceptional_point():
|
|
91
|
+
# The defining structural signature distinguishing a genuine
|
|
92
|
+
# exceptional point (eigenvector coalescence) from an incidental
|
|
93
|
+
# level crossing (eigenvectors stay independent): the right-
|
|
94
|
+
# eigenvector matrix's condition number should grow sharply as g
|
|
95
|
+
# approaches a detected transition.
|
|
96
|
+
eps = rmt.stats.find_exceptional_points(p=6, q=6, beta=2, seed=0, g_max=3.0, n_grid=3000)
|
|
97
|
+
g_star = eps[0]
|
|
98
|
+
cond_far = rmt.stats.eigenvector_condition_number(6, 6, 2, 0, g=max(g_star - 0.01, 0.0))
|
|
99
|
+
cond_near = rmt.stats.eigenvector_condition_number(6, 6, 2, 0, g=g_star + 5e-4)
|
|
100
|
+
assert cond_near > 2 * cond_far
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@pytest.mark.slow
|
|
104
|
+
def test_mean_exceptional_point_count_grows_with_system_size():
|
|
105
|
+
# Ensemble-level statistic: mean EP count over many independent
|
|
106
|
+
# realizations should grow with system size, roughly linearly in
|
|
107
|
+
# min(p, q) -- verified during development (p=q=4,8,12 gave ~4.2,
|
|
108
|
+
# 8.7, 13.1 over g in [0, 2]).
|
|
109
|
+
mean_small, err_small = rmt.stats.mean_exceptional_point_count(p=4, q=4, beta=2, g_max=2.0, n_realizations=8, n_grid=500, seed=0)
|
|
110
|
+
mean_large, err_large = rmt.stats.mean_exceptional_point_count(p=12, q=12, beta=2, g_max=2.0, n_realizations=8, n_grid=500, seed=1)
|
|
111
|
+
assert mean_small > 0
|
|
112
|
+
assert mean_large > 2 * mean_small
|
|
113
|
+
assert err_small >= 0 and err_large >= 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_fixed_realization_reconstruction_only_rescales_coupling_block():
|
|
117
|
+
# The trick find_exceptional_points/eigenvector_condition_number rely
|
|
118
|
+
# on: rebuilding the ensemble with a different g but the same seed
|
|
119
|
+
# reproduces the same A, D blocks, only rescaling the coupling block.
|
|
120
|
+
from physicskit.rmt.stats.pt_symmetric import _fixed_realization_matrix
|
|
121
|
+
|
|
122
|
+
p, q = 5, 5
|
|
123
|
+
h1 = _fixed_realization_matrix(p, q, 2, 42, g=0.1)
|
|
124
|
+
h2 = _fixed_realization_matrix(p, q, 2, 42, g=0.5)
|
|
125
|
+
np.testing.assert_allclose(h1[:p, :p], h2[:p, :p])
|
|
126
|
+
np.testing.assert_allclose(h1[p:, p:], h2[p:, p:])
|
|
127
|
+
np.testing.assert_allclose(h1[:p, p:] / 0.1, h2[:p, p:] / 0.5)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
# --- intermediate (semi-Poisson) spacing statistics ---
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_semi_poisson_is_normalized_with_unit_mean_spacing():
|
|
134
|
+
from scipy.integrate import quad
|
|
135
|
+
|
|
136
|
+
norm, _ = quad(rmt.stats.semi_poisson_pdf, 0, np.inf)
|
|
137
|
+
mean, _ = quad(lambda s: s * rmt.stats.semi_poisson_pdf(np.array([s]))[0], 0, np.inf)
|
|
138
|
+
assert norm == pytest.approx(1.0, abs=1e-8)
|
|
139
|
+
assert mean == pytest.approx(1.0, abs=1e-8)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_semi_poisson_cdf_is_derivative_consistent_with_pdf():
|
|
143
|
+
s = np.linspace(0.01, 5.0, 50)
|
|
144
|
+
h = 1e-6
|
|
145
|
+
finite_diff = (rmt.stats.semi_poisson_cdf(s + h) - rmt.stats.semi_poisson_cdf(s - h)) / (2 * h)
|
|
146
|
+
np.testing.assert_allclose(finite_diff, rmt.stats.semi_poisson_pdf(s), atol=1e-4)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_real_axis_spacings_matches_semi_poisson_better_than_poisson_or_goe():
|
|
150
|
+
# Verified during development: at small-to-moderate coupling g, the
|
|
151
|
+
# surviving real eigenvalues' spacing statistic is closest to
|
|
152
|
+
# semi-Poisson, not pure Poisson or pure GOE -- the "intermediate
|
|
153
|
+
# statistics" signature the design plan calls for.
|
|
154
|
+
ens = rmt.ensembles.PTSymmetricEnsemble(p=25, q=25, g=0.3, beta=2, seed=0)
|
|
155
|
+
spectrum = cached_sample(ens, n_samples=200)
|
|
156
|
+
spacings = rmt.stats.real_axis_spacings(spectrum.eigenvalues)
|
|
157
|
+
|
|
158
|
+
from scipy.stats import kstest
|
|
159
|
+
|
|
160
|
+
ks_semi_poisson = kstest(spacings, rmt.stats.semi_poisson_cdf).statistic
|
|
161
|
+
ks_poisson = kstest(spacings, lambda s: 1.0 - np.exp(-s)).statistic
|
|
162
|
+
ks_goe = kstest(spacings, lambda s: rmt.stats.wigner_surmise_cdf(s, beta=1)).statistic
|
|
163
|
+
|
|
164
|
+
assert ks_semi_poisson < ks_poisson
|
|
165
|
+
assert ks_semi_poisson < ks_goe
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_real_axis_spacings_skips_samples_with_too_few_real_eigenvalues():
|
|
169
|
+
# All-complex sample (no real eigenvalues at all) should not crash
|
|
170
|
+
# and should be skipped, not contribute spurious spacings.
|
|
171
|
+
eigenvalues = np.array(
|
|
172
|
+
[
|
|
173
|
+
[1.0 + 2.0j, 1.0 - 2.0j, 3.0 + 1.0j, 3.0 - 1.0j],
|
|
174
|
+
[0.0 + 0j, 1.0 + 0j, 2.0 + 0j, 3.0 + 0j],
|
|
175
|
+
]
|
|
176
|
+
)
|
|
177
|
+
spacings = rmt.stats.real_axis_spacings(eigenvalues)
|
|
178
|
+
np.testing.assert_allclose(spacings, [1.0, 1.0, 1.0])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Coverage for physicskit.rmt.utils.random_state.as_generator: the
|
|
2
|
+
existing-Generator passthrough branch (every ensemble test seeds with a
|
|
3
|
+
plain int, so this was never exercised)."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from physicskit.rmt.utils.random_state import as_generator
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_as_generator_returns_an_existing_generator_unchanged():
|
|
13
|
+
rng = np.random.default_rng(42)
|
|
14
|
+
assert as_generator(rng) is rng
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_as_generator_coerces_an_int_seed():
|
|
18
|
+
rng = as_generator(7)
|
|
19
|
+
assert isinstance(rng, np.random.Generator)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Tests for the real-Ginibre real-eigenvalue-count statistic and
|
|
2
|
+
benchmark.
|
|
3
|
+
|
|
4
|
+
The asymptotic formula sqrt(2n/pi) + 1/2 was fit against, and confirmed
|
|
5
|
+
by, direct Monte Carlo real-eigenvalue counts at n = 2..128 during
|
|
6
|
+
development (see ``physicskit.rmt.stats.real_ginibre`` module docstring) -- it
|
|
7
|
+
is NOT the exact Edelman-Kostlan-Shub finite-n closed form, so tests use
|
|
8
|
+
a loose tolerance and larger n rather than expecting near-exact
|
|
9
|
+
agreement.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import pytest
|
|
14
|
+
from cache_utils import cached_sample
|
|
15
|
+
|
|
16
|
+
import physicskit.rmt as rmt
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_real_eigenvalue_count_empirical_on_known_matrix():
|
|
20
|
+
# A diagonal (all-real-eigenvalue) "spectrum": every eigenvalue
|
|
21
|
+
# should be counted as real.
|
|
22
|
+
eigenvalues = np.array([[1.0 + 0j, 2.0 + 0j, -3.0 + 0j]])
|
|
23
|
+
counts = rmt.stats.real_eigenvalue_count_empirical(eigenvalues)
|
|
24
|
+
np.testing.assert_array_equal(counts, [3])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_real_eigenvalue_count_empirical_mixed():
|
|
28
|
+
eigenvalues = np.array([[1.0 + 0j, 2.0 + 3.0j, 2.0 - 3.0j, -1.0 + 0j]])
|
|
29
|
+
counts = rmt.stats.real_eigenvalue_count_empirical(eigenvalues)
|
|
30
|
+
np.testing.assert_array_equal(counts, [2])
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_asymptotic_matches_n2_exact_value():
|
|
34
|
+
# n=2 has a well-known exact closed form: E[N_real] = sqrt(2)
|
|
35
|
+
# (P(both eigenvalues real) = 1/sqrt(2)). The asymptotic formula is
|
|
36
|
+
# not expected to be exact this low, but should be in the right
|
|
37
|
+
# ballpark.
|
|
38
|
+
theory = rmt.stats.real_eigenvalue_count_asymptotic(2)
|
|
39
|
+
assert theory == pytest.approx(np.sqrt(2), abs=0.3)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_asymptotic_matches_monte_carlo_at_moderate_n():
|
|
43
|
+
n = 64
|
|
44
|
+
ens = rmt.ensembles.GinOE(n=n, seed=0)
|
|
45
|
+
spectrum = cached_sample(ens, n_samples=2000)
|
|
46
|
+
counts = rmt.stats.real_eigenvalue_count_empirical(spectrum.eigenvalues)
|
|
47
|
+
empirical_mean = counts.mean()
|
|
48
|
+
theory = rmt.stats.real_eigenvalue_count_asymptotic(n)
|
|
49
|
+
assert empirical_mean == pytest.approx(theory, rel=0.1)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_real_ginibre_eigenvalue_count_benchmark():
|
|
53
|
+
n = 100
|
|
54
|
+
ens = rmt.ensembles.GinOE(n=n, seed=1)
|
|
55
|
+
spectrum = cached_sample(ens, n_samples=2000)
|
|
56
|
+
benchmark = rmt.validation.RealGinibreEigenvalueCount()
|
|
57
|
+
result = benchmark.validate(spectrum)
|
|
58
|
+
assert result.n == n
|
|
59
|
+
assert result.relative_error < 0.1
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_mean_real_count_grows_like_sqrt_n():
|
|
63
|
+
# The qualitative signature: E[N_real] should grow much slower than
|
|
64
|
+
# n itself (like sqrt(n)), unlike a generic extensive quantity.
|
|
65
|
+
small = rmt.stats.real_eigenvalue_count_asymptotic(25)
|
|
66
|
+
large = rmt.stats.real_eigenvalue_count_asymptotic(100)
|
|
67
|
+
# n quadruples (25->100); sqrt(n) doubles, so the count should
|
|
68
|
+
# roughly double too, not quadruple.
|
|
69
|
+
assert 1.5 < large / small < 2.5
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# --- real-eigenvalue density (not just count) ---
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_density_asymptotic_is_zero_outside_unit_disk():
|
|
76
|
+
n = 100
|
|
77
|
+
x = np.array([-1.5, -1.01, 1.01, 1.5])
|
|
78
|
+
density = rmt.stats.real_eigenvalue_density_asymptotic(x, n)
|
|
79
|
+
np.testing.assert_array_equal(density, 0.0)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_density_asymptotic_integrates_to_the_count():
|
|
83
|
+
# The uniform-box density's total integral (height * width 2) should
|
|
84
|
+
# equal the already-verified total count formula.
|
|
85
|
+
n = 200
|
|
86
|
+
x = np.linspace(-1.0, 1.0, 20001)
|
|
87
|
+
density = rmt.stats.real_eigenvalue_density_asymptotic(x, n)
|
|
88
|
+
integral = np.trapezoid(density, x)
|
|
89
|
+
assert integral == pytest.approx(rmt.stats.real_eigenvalue_count_asymptotic(n), rel=1e-3)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_density_empirical_matches_asymptotic_in_the_bulk():
|
|
93
|
+
n = 300
|
|
94
|
+
ens = rmt.ensembles.GinOE(n=n, seed=0)
|
|
95
|
+
spectrum = cached_sample(ens, n_samples=2000)
|
|
96
|
+
centers, density = rmt.stats.real_eigenvalue_density_empirical(spectrum.eigenvalues, n)
|
|
97
|
+
theory = rmt.stats.real_eigenvalue_density_asymptotic(centers, n)
|
|
98
|
+
|
|
99
|
+
bulk = np.abs(centers) < 0.8
|
|
100
|
+
assert density[bulk].mean() == pytest.approx(theory[bulk].mean(), rel=0.15)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def test_density_empirical_drops_near_the_edge():
|
|
104
|
+
# Qualitative edge-cutoff check: density well outside the unit disk
|
|
105
|
+
# (after rescaling by sqrt(n)) should be much smaller than in the bulk.
|
|
106
|
+
n = 300
|
|
107
|
+
ens = rmt.ensembles.GinOE(n=n, seed=0)
|
|
108
|
+
spectrum = cached_sample(ens, n_samples=2000)
|
|
109
|
+
centers, density = rmt.stats.real_eigenvalue_density_empirical(spectrum.eigenvalues, n, x_max=1.3)
|
|
110
|
+
bulk_density = density[np.abs(centers) < 0.5].mean()
|
|
111
|
+
edge_density = density[np.abs(centers) > 1.15].mean()
|
|
112
|
+
assert edge_density < 0.1 * bulk_density
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Tests for spectral rigidity (number variance) and universality
|
|
2
|
+
testing.
|
|
3
|
+
|
|
4
|
+
The GUE number variance formula is *exact* (derived directly from the
|
|
5
|
+
already-validated sine kernel, not an approximation), so it's tested
|
|
6
|
+
against a tight tolerance. The GOE formula is confirmed by two
|
|
7
|
+
independent secondary sources; the GSE formula follows the same
|
|
8
|
+
commonly-cited pattern but was verified only empirically here (against
|
|
9
|
+
Monte Carlo GSE) before being trusted -- both are large-L asymptotic,
|
|
10
|
+
so tested with a looser tolerance and only at moderately large L.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pytest
|
|
15
|
+
from cache_utils import cached_sample
|
|
16
|
+
|
|
17
|
+
import physicskit.rmt as rmt
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_poisson_number_variance_is_exactly_l():
|
|
21
|
+
l_values = [1, 5, 10, 50]
|
|
22
|
+
np.testing.assert_allclose(rmt.stats.number_variance_poisson(l_values), l_values)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_gue_number_variance_much_smaller_than_poisson():
|
|
26
|
+
# The defining qualitative signature of spectral rigidity: GUE's
|
|
27
|
+
# number variance grows logarithmically, Poisson's linearly, so at
|
|
28
|
+
# any reasonably large L, GUE's variance is far smaller.
|
|
29
|
+
l_values = [10, 50, 100]
|
|
30
|
+
gue = rmt.stats.number_variance_gue_exact(l_values)
|
|
31
|
+
poisson = rmt.stats.number_variance_poisson(l_values)
|
|
32
|
+
assert np.all(gue < poisson / 3)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_gue_exact_matches_empirical_window_counting():
|
|
36
|
+
ens = rmt.ensembles.GUE(n=4000, seed=1)
|
|
37
|
+
spectrum = cached_sample(ens, n_samples=15)
|
|
38
|
+
l_values = [2, 5, 10, 20]
|
|
39
|
+
theory = rmt.stats.number_variance_gue_exact(l_values)
|
|
40
|
+
empirical = rmt.stats.number_variance_empirical(spectrum, rmt.stats.semicircle_cdf, l_values, n_windows=200, seed=0)
|
|
41
|
+
np.testing.assert_allclose(theory, empirical, atol=0.05)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_goe_asymptotic_matches_empirical_at_large_l():
|
|
45
|
+
ens = rmt.ensembles.GOE(n=4000, seed=2)
|
|
46
|
+
spectrum = cached_sample(ens, n_samples=15)
|
|
47
|
+
l_values = [5, 10, 20]
|
|
48
|
+
theory = rmt.stats.number_variance_goe_asymptotic(l_values)
|
|
49
|
+
empirical = rmt.stats.number_variance_empirical(spectrum, rmt.stats.semicircle_cdf, l_values, n_windows=300, seed=0)
|
|
50
|
+
np.testing.assert_allclose(theory, empirical, atol=0.06)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_gse_asymptotic_matches_empirical_at_large_l():
|
|
54
|
+
ens = rmt.ensembles.GSE(n=4000, seed=3)
|
|
55
|
+
spectrum = cached_sample(ens, n_samples=15)
|
|
56
|
+
l_values = [5, 10, 20]
|
|
57
|
+
theory = rmt.stats.number_variance_gse_asymptotic(l_values)
|
|
58
|
+
empirical = rmt.stats.number_variance_empirical(spectrum, rmt.stats.semicircle_cdf, l_values, n_windows=300, seed=0)
|
|
59
|
+
np.testing.assert_allclose(theory, empirical, atol=0.03)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_number_variance_theory_dispatch():
|
|
63
|
+
l_values = [10]
|
|
64
|
+
np.testing.assert_allclose(
|
|
65
|
+
rmt.stats.number_variance_theory(l_values, beta=1),
|
|
66
|
+
rmt.stats.number_variance_goe_asymptotic(l_values),
|
|
67
|
+
)
|
|
68
|
+
np.testing.assert_allclose(
|
|
69
|
+
rmt.stats.number_variance_theory(l_values, beta=2),
|
|
70
|
+
rmt.stats.number_variance_gue_exact(l_values),
|
|
71
|
+
)
|
|
72
|
+
np.testing.assert_allclose(
|
|
73
|
+
rmt.stats.number_variance_theory(l_values, beta=4),
|
|
74
|
+
rmt.stats.number_variance_gse_asymptotic(l_values),
|
|
75
|
+
)
|
|
76
|
+
with pytest.raises(ValueError):
|
|
77
|
+
rmt.stats.number_variance_theory(l_values, beta=3)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
# --- spectral rigidity (Delta_3) ---
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_poisson_delta3_is_exactly_l_over_15():
|
|
84
|
+
l_values = [3, 15, 30, 60]
|
|
85
|
+
np.testing.assert_allclose(rmt.stats.spectral_rigidity_poisson(l_values), np.array(l_values) / 15.0)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_poisson_delta3_empirical_matches_exact():
|
|
89
|
+
ens = rmt.ensembles.PoissonEnsemble(n=4000, seed=21)
|
|
90
|
+
spectrum = cached_sample(ens, n_samples=8)
|
|
91
|
+
l_values = [5, 10, 20, 40]
|
|
92
|
+
|
|
93
|
+
def identity_cdf(x):
|
|
94
|
+
return x / spectrum.n
|
|
95
|
+
|
|
96
|
+
empirical = rmt.stats.spectral_rigidity_empirical(spectrum, identity_cdf, l_values, n_windows=300, seed=0)
|
|
97
|
+
theory = rmt.stats.spectral_rigidity_poisson(l_values)
|
|
98
|
+
np.testing.assert_allclose(theory, empirical, rtol=0.08)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@pytest.mark.slow
|
|
102
|
+
def test_gue_delta3_theory_matches_empirical():
|
|
103
|
+
ens = rmt.ensembles.GUE(n=150, seed=22)
|
|
104
|
+
spectrum = cached_sample(ens, n_samples=20)
|
|
105
|
+
l_values = [3, 5, 8, 12]
|
|
106
|
+
theory = rmt.stats.spectral_rigidity_theory(l_values, beta=2)
|
|
107
|
+
empirical = rmt.stats.spectral_rigidity_empirical(spectrum, rmt.stats.semicircle_cdf, l_values, n_windows=200, seed=0)
|
|
108
|
+
np.testing.assert_allclose(theory, empirical, rtol=0.05)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@pytest.mark.slow
|
|
112
|
+
def test_goe_delta3_theory_matches_empirical_at_large_l():
|
|
113
|
+
ens = rmt.ensembles.GOE(n=200, seed=23)
|
|
114
|
+
spectrum = cached_sample(ens, n_samples=20)
|
|
115
|
+
l_values = [8, 12, 16, 20]
|
|
116
|
+
theory = rmt.stats.spectral_rigidity_theory(l_values, beta=1)
|
|
117
|
+
empirical = rmt.stats.spectral_rigidity_empirical(spectrum, rmt.stats.semicircle_cdf, l_values, n_windows=250, seed=0)
|
|
118
|
+
np.testing.assert_allclose(theory, empirical, rtol=0.1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@pytest.mark.slow
|
|
122
|
+
def test_gue_delta3_much_smaller_than_poisson():
|
|
123
|
+
l_values = [10, 50, 100]
|
|
124
|
+
gue = rmt.stats.spectral_rigidity_theory(l_values, beta=2)
|
|
125
|
+
poisson = rmt.stats.spectral_rigidity_poisson(l_values)
|
|
126
|
+
assert np.all(gue < poisson / 3)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# --- universality ---
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@pytest.mark.slow
|
|
133
|
+
@pytest.mark.parametrize("beta", [1, 2])
|
|
134
|
+
def test_universality_holds_across_entry_distributions(beta):
|
|
135
|
+
result = rmt.validation.check_universality(n=300, beta=beta, n_samples=10, seed=5)
|
|
136
|
+
assert set(result.per_distribution.keys()) == set(rmt.validation.DEFAULT_ENTRY_DISTRIBUTIONS.keys())
|
|
137
|
+
assert result.max_ks_statistic < 0.035
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@pytest.mark.parametrize("beta", [1, 2])
|
|
141
|
+
def test_general_wigner_ensemble_matches_semicircle_directly(beta):
|
|
142
|
+
ens = rmt.ensembles.GeneralWignerEnsemble(n=1000, entry_sampler=rmt.ensembles.rademacher, beta=beta, seed=7)
|
|
143
|
+
spectrum = cached_sample(ens, n_samples=20)
|
|
144
|
+
benchmark = rmt.validation.WignerSemicircle()
|
|
145
|
+
result = benchmark.validate(spectrum, seed=7)
|
|
146
|
+
assert result.ks_statistic < 0.01
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def test_general_wigner_ensemble_rejects_beta_4():
|
|
150
|
+
with pytest.raises(ValueError):
|
|
151
|
+
rmt.ensembles.GeneralWignerEnsemble(n=100, entry_sampler=rmt.ensembles.rademacher, beta=4, seed=0)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_asymmetric_exponential_entries_still_converge():
|
|
155
|
+
# The strongest universality stress test in the default set: a
|
|
156
|
+
# skewed, unbounded entry distribution should still give the
|
|
157
|
+
# symmetric semicircle law.
|
|
158
|
+
ens = rmt.ensembles.GeneralWignerEnsemble(
|
|
159
|
+
n=1000,
|
|
160
|
+
entry_sampler=rmt.ensembles.exponential_centered_unit_variance,
|
|
161
|
+
beta=2,
|
|
162
|
+
seed=11,
|
|
163
|
+
)
|
|
164
|
+
spectrum = cached_sample(ens, n_samples=20)
|
|
165
|
+
benchmark = rmt.validation.WignerSemicircle()
|
|
166
|
+
result = benchmark.validate(spectrum, seed=11)
|
|
167
|
+
assert result.ks_statistic < 0.01
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Tests for the single ring theorem and NonHermitianWishartEnsemble.
|
|
2
|
+
|
|
3
|
+
The exact Marchenko-Pastur moment identities E[X] = 1 and
|
|
4
|
+
E[1/X] = 1/(1-gamma) (gamma < 1), which give the theoretical ring radii
|
|
5
|
+
r_out=1, r_in=sqrt(1-gamma), were verified numerically against direct
|
|
6
|
+
Monte Carlo before being used here (see
|
|
7
|
+
``physicskit.rmt.stats.single_ring`` and ``physicskit.rmt.ensembles.single_ring`` module
|
|
8
|
+
docstrings). Like every other limiting-law benchmark in this package,
|
|
9
|
+
the single ring theorem is an N -> infinity result, so tests check that
|
|
10
|
+
KS distance to the theoretical annulus SHRINKS with increasing N, not
|
|
11
|
+
that it passes a fixed small-N threshold (confirmed during development:
|
|
12
|
+
KS went from 0.046 at n=150 to 0.015 at n=1200 for a fixed gamma).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
import pytest
|
|
17
|
+
from cache_utils import cached_sample
|
|
18
|
+
from scipy.stats import linregress
|
|
19
|
+
|
|
20
|
+
import physicskit.rmt as rmt
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_wishart_moment_identities():
|
|
24
|
+
# E[X] = 1 and E[1/X] = 1/(1-gamma) for the Marchenko-Pastur
|
|
25
|
+
# distribution -- the exact facts the theoretical ring radii rest
|
|
26
|
+
# on. Checked directly here against a fresh Monte Carlo sample,
|
|
27
|
+
# independent of the ensemble/benchmark machinery.
|
|
28
|
+
from physicskit.rmt.utils.tridiagonal import sample_laguerre_beta_eigenvalues
|
|
29
|
+
|
|
30
|
+
rng = np.random.default_rng(0)
|
|
31
|
+
n, m = 500, 1000
|
|
32
|
+
gamma = n / m
|
|
33
|
+
eigs = sample_laguerre_beta_eigenvalues(m, n, 2.0, rng)
|
|
34
|
+
assert eigs.mean() == pytest.approx(1.0, rel=0.02)
|
|
35
|
+
assert np.mean(1.0 / eigs) == pytest.approx(1.0 / (1.0 - gamma), rel=0.02)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_single_ring_radii_from_uniform_singular_values():
|
|
39
|
+
# Constant singular values s=1 for everything -> a genuine circle
|
|
40
|
+
# (r_in = r_out = 1), the trivial sanity check of the raw formula.
|
|
41
|
+
r_in, r_out = rmt.stats.single_ring_radii(np.ones(50))
|
|
42
|
+
assert r_in == pytest.approx(1.0)
|
|
43
|
+
assert r_out == pytest.approx(1.0)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_single_ring_radii_matches_wishart_theory():
|
|
47
|
+
gamma = 0.4
|
|
48
|
+
r_in, r_out = rmt.stats.single_ring_radii_wishart_theory(gamma)
|
|
49
|
+
assert r_out == 1.0
|
|
50
|
+
assert r_in == pytest.approx(np.sqrt(0.6))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_single_ring_radii_rejects_invalid_gamma():
|
|
54
|
+
with pytest.raises(ValueError):
|
|
55
|
+
rmt.stats.single_ring_radii_wishart_theory(1.0)
|
|
56
|
+
with pytest.raises(ValueError):
|
|
57
|
+
rmt.stats.single_ring_radii_wishart_theory(0.0)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_nonhermitian_wishart_rejects_gamma_geq_1():
|
|
61
|
+
with pytest.raises(ValueError):
|
|
62
|
+
rmt.ensembles.NonHermitianWishartEnsemble(n=100, m=100, seed=0)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_nonhermitian_wishart_rejects_nonpositive_beta():
|
|
66
|
+
with pytest.raises(ValueError):
|
|
67
|
+
rmt.ensembles.NonHermitianWishartEnsemble(n=10, m=20, beta=0.0, seed=0)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_single_ring_ensemble_and_nonhermitian_wishart_fresh_samples_are_finite():
|
|
71
|
+
# Fresh (uncached) direct .sample() calls, to actually exercise
|
|
72
|
+
# _sample_eigenvalues/natural_scale rather than risk a cache hit.
|
|
73
|
+
ens1 = rmt.ensembles.SingleRingEnsemble(n=6, singular_value_sampler=lambda rng, n: np.ones(n), seed=321)
|
|
74
|
+
spectrum1 = ens1.sample(n_samples=2)
|
|
75
|
+
assert np.all(np.isfinite(spectrum1.eigenvalues))
|
|
76
|
+
assert ens1.natural_scale() == 1.0
|
|
77
|
+
|
|
78
|
+
ens2 = rmt.ensembles.NonHermitianWishartEnsemble(n=6, m=12, seed=321)
|
|
79
|
+
spectrum2 = ens2.sample(n_samples=2)
|
|
80
|
+
assert np.all(np.isfinite(spectrum2.eigenvalues))
|
|
81
|
+
assert ens2.natural_scale() == 1.0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_nonhermitian_wishart_eigenvalue_radii_stay_within_ring_edges():
|
|
85
|
+
n, m = 400, 1000
|
|
86
|
+
ens = rmt.ensembles.NonHermitianWishartEnsemble(n=n, m=m, seed=1)
|
|
87
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
88
|
+
r_in, r_out = rmt.stats.single_ring_radii_wishart_theory(ens.gamma)
|
|
89
|
+
radii = np.abs(spectrum.eigenvalues.ravel())
|
|
90
|
+
# A modest margin for finite-n boundary fluctuation (the theorem is
|
|
91
|
+
# an n -> infinity statement about the support, not a hard bound at
|
|
92
|
+
# finite n).
|
|
93
|
+
assert radii.min() > r_in - 0.15
|
|
94
|
+
assert radii.max() < r_out + 0.15
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_single_ring_theorem_convergence_shrinks_with_n():
|
|
98
|
+
gamma_fixed = 0.5
|
|
99
|
+
n_values = [100, 300, 900]
|
|
100
|
+
r_in, r_out = rmt.stats.single_ring_radii_wishart_theory(gamma_fixed)
|
|
101
|
+
benchmark = rmt.validation.SingleRingTheorem(r_in=r_in, r_out=r_out)
|
|
102
|
+
|
|
103
|
+
results = []
|
|
104
|
+
for n in n_values:
|
|
105
|
+
m = int(n / gamma_fixed)
|
|
106
|
+
ens = rmt.ensembles.NonHermitianWishartEnsemble(n=n, m=m, seed=5)
|
|
107
|
+
spectrum = cached_sample(ens, n_samples=max(2, 3000 // n))
|
|
108
|
+
results.append(benchmark.validate(spectrum, seed=5))
|
|
109
|
+
|
|
110
|
+
ks_stats = np.array([r.ks_statistic for r in results])
|
|
111
|
+
slope = linregress(np.log(n_values), np.log(ks_stats)).slope
|
|
112
|
+
assert slope < -0.2, f"expected shrinking KS distance, slope={slope}"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_single_ring_theorem_rejects_invalid_radii():
|
|
116
|
+
with pytest.raises(ValueError):
|
|
117
|
+
rmt.validation.SingleRingTheorem(r_in=1.0, r_out=0.5)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_balanced_gamma_one_collapses_to_filled_disk():
|
|
121
|
+
# gamma -> 1 (e.g. via a plain SingleRingEnsemble with Wishart-type
|
|
122
|
+
# singular values at m=n) should give r_in ~ 0, consistent with the
|
|
123
|
+
# ordinary (Ginibre) circular law rather than a genuine ring.
|
|
124
|
+
from physicskit.rmt.utils.tridiagonal import sample_laguerre_beta_eigenvalues
|
|
125
|
+
|
|
126
|
+
def wishart_singular_values(rng, n):
|
|
127
|
+
return np.sqrt(sample_laguerre_beta_eigenvalues(n, n, 2.0, rng))
|
|
128
|
+
|
|
129
|
+
ens = rmt.ensembles.SingleRingEnsemble(n=300, singular_value_sampler=wishart_singular_values, seed=2)
|
|
130
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
131
|
+
radii = np.abs(spectrum.eigenvalues.ravel())
|
|
132
|
+
assert radii.min() < 0.1 # close to 0, not bounded away like a genuine ring
|