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,127 @@
|
|
|
1
|
+
"""Tests for the Gaussian ensembles (GOE/GUE/GSE) and the Wigner semicircle
|
|
2
|
+
benchmark.
|
|
3
|
+
|
|
4
|
+
Per the design plan (see ``physicskit.rmt.validation.base``), we do NOT gate on a
|
|
5
|
+
single p > 0.05 KS test at one fixed N -- KS power grows with N, so that
|
|
6
|
+
criterion becomes *less* likely to pass as N grows, which is backwards for
|
|
7
|
+
demonstrating asymptotic convergence. Instead we assert that the distance
|
|
8
|
+
to the theoretical semicircle law shrinks as N increases.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
from cache_utils import cached_sample
|
|
14
|
+
from scipy.stats import linregress
|
|
15
|
+
|
|
16
|
+
import physicskit.rmt as rmt
|
|
17
|
+
|
|
18
|
+
CLASSICAL_ENSEMBLES = [
|
|
19
|
+
("GOE", rmt.ensembles.GOE, 1.0),
|
|
20
|
+
("GUE", rmt.ensembles.GUE, 2.0),
|
|
21
|
+
("GSE", rmt.ensembles.GSE, 4.0),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_ENSEMBLES)
|
|
26
|
+
def test_beta_is_correct(name, cls, expected_beta):
|
|
27
|
+
ens = cls(n=50, seed=0)
|
|
28
|
+
assert ens.beta == expected_beta
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_ENSEMBLES)
|
|
32
|
+
def test_sample_shape_and_reproducibility(name, cls, expected_beta):
|
|
33
|
+
ens_a = cls(n=200, seed=123)
|
|
34
|
+
ens_b = cls(n=200, seed=123)
|
|
35
|
+
spec_a = ens_a.sample(n_samples=5)
|
|
36
|
+
spec_b = ens_b.sample(n_samples=5)
|
|
37
|
+
|
|
38
|
+
assert spec_a.eigenvalues.shape == (5, 200)
|
|
39
|
+
# same seed -> identical draws
|
|
40
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_ENSEMBLES)
|
|
44
|
+
def test_eigenvalues_are_sorted_and_real(name, cls, expected_beta):
|
|
45
|
+
ens = cls(n=100, seed=1)
|
|
46
|
+
spectrum = cached_sample(ens, n_samples=3)
|
|
47
|
+
for row in spectrum.eigenvalues:
|
|
48
|
+
assert np.all(np.isfinite(row))
|
|
49
|
+
assert np.all(np.diff(row) >= 0) # eigh_tridiagonal returns ascending order
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_ENSEMBLES)
|
|
53
|
+
def test_rescaled_support_approaches_semicircle_radius(name, cls, expected_beta):
|
|
54
|
+
# At large n, essentially all rescaled eigenvalues should fall within
|
|
55
|
+
# [-2, 2] (+ small finite-size overshoot), and the extremes should be
|
|
56
|
+
# close to +/-2 (Tracy-Widom edge fluctuations are O(n^{-2/3}), much
|
|
57
|
+
# smaller than the bulk scale checked here).
|
|
58
|
+
ens = cls(n=3000, seed=2)
|
|
59
|
+
spectrum = cached_sample(ens, n_samples=1)
|
|
60
|
+
rescaled = spectrum.rescaled.ravel()
|
|
61
|
+
assert rescaled.max() < 2.5
|
|
62
|
+
assert rescaled.min() > -2.5
|
|
63
|
+
assert rescaled.max() > 1.8
|
|
64
|
+
assert rescaled.min() < -1.8
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@pytest.mark.parametrize("name,cls,expected_beta", CLASSICAL_ENSEMBLES)
|
|
68
|
+
def test_semicircle_convergence_rate_decreases_with_n(name, cls, expected_beta):
|
|
69
|
+
# NOTE on methodology: eigenvalues within a single sampled matrix are
|
|
70
|
+
# NOT independent (level repulsion correlates them), so the
|
|
71
|
+
# distance-to-theory at any *single* N is a noisy statistic -- a
|
|
72
|
+
# strict pairwise decrease across only 2-3 N values is not a
|
|
73
|
+
# statistically robust thing to assert (it can fail by chance even
|
|
74
|
+
# when the ensemble is implemented correctly). Instead, fit the
|
|
75
|
+
# log-log trend across five N values and require a clearly negative
|
|
76
|
+
# slope; this is the same "convergence curve, not a single p > 0.05
|
|
77
|
+
# gate" philosophy from the design plan, made robust to per-point noise.
|
|
78
|
+
#
|
|
79
|
+
# Looped manually here (rather than via benchmark.convergence_curve)
|
|
80
|
+
# so each N's sample can be disk-cached individually.
|
|
81
|
+
benchmark = rmt.validation.WignerSemicircle()
|
|
82
|
+
n_values = [100, 300, 900, 2700, 8100]
|
|
83
|
+
results = []
|
|
84
|
+
for n in n_values:
|
|
85
|
+
ens = cls(n=n, seed=42)
|
|
86
|
+
spectrum = cached_sample(ens, n_samples=30)
|
|
87
|
+
results.append(benchmark.validate(spectrum, seed=42))
|
|
88
|
+
|
|
89
|
+
ks_stats = np.array([r.ks_statistic for r in results])
|
|
90
|
+
wasserstein = np.array([r.wasserstein_distance for r in results])
|
|
91
|
+
|
|
92
|
+
ks_slope = linregress(np.log(n_values), np.log(ks_stats)).slope
|
|
93
|
+
# KS convergence is the clean, low-noise signal here: theory predicts
|
|
94
|
+
# roughly -1/2 to -1; require a clearly negative trend well short of
|
|
95
|
+
# that range to keep the test robust to run-to-run noise.
|
|
96
|
+
assert ks_slope < -0.3, f"{name}: expected shrinking KS distance, slope={ks_slope}"
|
|
97
|
+
|
|
98
|
+
# Wasserstein distance (vs a large fixed reference sample) is a noisier
|
|
99
|
+
# signal at this sample budget -- use it as a coarser sanity check
|
|
100
|
+
# (final smaller than initial) rather than a strict per-step trend.
|
|
101
|
+
assert wasserstein[-1] < wasserstein[0]
|
|
102
|
+
|
|
103
|
+
# By the largest N tested, agreement should already be tight in
|
|
104
|
+
# absolute terms. The Wasserstein floor here is set by residual
|
|
105
|
+
# Monte Carlo noise (correlated eigenvalues within a sample reduce
|
|
106
|
+
# the effective sample size below n * n_samples), not by any
|
|
107
|
+
# remaining bias -- verified empirically to sit around 0.002-0.003
|
|
108
|
+
# at n=8100 with n_samples=30 for GOE/GUE/GSE.
|
|
109
|
+
assert ks_stats[-1] < 0.001
|
|
110
|
+
assert wasserstein[-1] < 0.005
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_continuum_beta_also_matches_semicircle():
|
|
114
|
+
# beta need not be 1, 2, or 4 -- the semicircle law (and this
|
|
115
|
+
# construction) holds for any beta > 0.
|
|
116
|
+
ens = rmt.ensembles.HermiteBetaEnsemble(n=2000, beta=1.7, seed=3)
|
|
117
|
+
spectrum = cached_sample(ens, n_samples=25)
|
|
118
|
+
benchmark = rmt.validation.WignerSemicircle()
|
|
119
|
+
result = benchmark.validate(spectrum, seed=3)
|
|
120
|
+
assert result.ks_statistic < 0.01
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def test_invalid_beta_rejected():
|
|
124
|
+
with pytest.raises(ValueError):
|
|
125
|
+
rmt.ensembles.HermiteBetaEnsemble(n=100, beta=0, seed=0)
|
|
126
|
+
with pytest.raises(ValueError):
|
|
127
|
+
rmt.ensembles.HermiteBetaEnsemble(n=100, beta=-1, seed=0)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Tests for GinOE, GinUE, GinSE and the circular law benchmark.
|
|
2
|
+
|
|
3
|
+
Circular law convergence is known to be slower than the Hermitian
|
|
4
|
+
ensembles' semicircle/Marchenko-Pastur convergence (non-Hermitian
|
|
5
|
+
matrices have weaker eigenvalue rigidity) -- confirmed during
|
|
6
|
+
development (log-log KS slope ~ -0.37 vs ~ -0.85 to -0.97 for the
|
|
7
|
+
semicircle law at comparable N). Thresholds here are calibrated to that
|
|
8
|
+
genuinely slower rate, not loosened arbitrarily.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
import pytest
|
|
13
|
+
from cache_utils import cached_sample
|
|
14
|
+
from scipy.stats import linregress
|
|
15
|
+
|
|
16
|
+
import physicskit.rmt as rmt
|
|
17
|
+
|
|
18
|
+
GINIBRE = [
|
|
19
|
+
("GinOE", rmt.ensembles.GinOE, 1),
|
|
20
|
+
("GinUE", rmt.ensembles.GinUE, 2),
|
|
21
|
+
("GinSE", rmt.ensembles.GinSE, 4),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.mark.parametrize("name,cls,beta", GINIBRE)
|
|
26
|
+
def test_beta_is_correct(name, cls, beta):
|
|
27
|
+
ens = cls(n=30, seed=0)
|
|
28
|
+
assert ens.beta == beta
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize("name,cls,beta", GINIBRE)
|
|
32
|
+
def test_eigenvalues_are_complex(name, cls, beta):
|
|
33
|
+
ens = cls(n=50, seed=1)
|
|
34
|
+
spectrum = cached_sample(ens, n_samples=3)
|
|
35
|
+
assert np.iscomplexobj(spectrum.eigenvalues)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_ginse_produces_2n_eigenvalues():
|
|
39
|
+
# Quaternion Ginibre: n x n quaternion matrix -> 2n complex
|
|
40
|
+
# eigenvalues per sample (genuinely distinct conjugate pairs, NOT
|
|
41
|
+
# deduplicated -- see ensembles/ginibre.py docstring for why this
|
|
42
|
+
# differs from GSE/CSE's true degeneracy).
|
|
43
|
+
ens = rmt.ensembles.GinSE(n=40, seed=2)
|
|
44
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
45
|
+
assert spectrum.eigenvalues.shape == (5, 80)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_ginse_eigenvalues_form_conjugate_pairs_but_are_distinct():
|
|
49
|
+
ens = rmt.ensembles.GinSE(n=30, seed=3)
|
|
50
|
+
spectrum = cached_sample(ens, n_samples=1)
|
|
51
|
+
eigs = spectrum.eigenvalues[0]
|
|
52
|
+
# sort by real then imaginary part; conjugate pairs should be
|
|
53
|
+
# adjacent, with matching real parts and opposite imaginary parts
|
|
54
|
+
order = np.lexsort((eigs.imag, eigs.real))
|
|
55
|
+
sorted_eigs = eigs[order]
|
|
56
|
+
# Rough structural check: for each non-real eigenvalue, its conjugate
|
|
57
|
+
# should also be present in the spectrum.
|
|
58
|
+
non_real = sorted_eigs[np.abs(sorted_eigs.imag) > 1e-6]
|
|
59
|
+
conjugates_present = 0
|
|
60
|
+
for z in non_real:
|
|
61
|
+
if np.any(np.abs(sorted_eigs - np.conj(z)) < 1e-8):
|
|
62
|
+
conjugates_present += 1
|
|
63
|
+
assert conjugates_present == len(non_real)
|
|
64
|
+
# And distinct: no exact duplicate values (unlike GSE/CSE degeneracy)
|
|
65
|
+
assert len(np.unique(np.round(eigs, 10))) == len(eigs)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("name,cls,beta", GINIBRE)
|
|
69
|
+
def test_reproducibility(name, cls, beta):
|
|
70
|
+
ens_a = cls(n=60, seed=42)
|
|
71
|
+
ens_b = cls(n=60, seed=42)
|
|
72
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
73
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
74
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("name,cls,beta", GINIBRE)
|
|
78
|
+
def test_radii_approach_unit_disk_at_large_n(name, cls, beta):
|
|
79
|
+
# NOTE: GinSE embeds an n x n quaternion matrix as a 2n x 2n complex
|
|
80
|
+
# matrix, so its dense eigensolve cost is ~8x that of GinOE/GinUE at
|
|
81
|
+
# the same n -- keep n modest here rather than reusing a size tuned
|
|
82
|
+
# for the real/complex cases.
|
|
83
|
+
n = 400
|
|
84
|
+
ens = cls(n=n, seed=4)
|
|
85
|
+
spectrum = cached_sample(ens, n_samples=1)
|
|
86
|
+
radii = np.abs(spectrum.rescaled.ravel())
|
|
87
|
+
assert radii.max() < 1.35
|
|
88
|
+
assert radii.mean() == pytest.approx(2.0 / 3.0, abs=0.06) # E[r] for uniform disk = 2/3
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@pytest.mark.parametrize("name,cls,beta", GINIBRE)
|
|
92
|
+
def test_circular_law_convergence_shrinks_with_n(name, cls, beta):
|
|
93
|
+
# Looped manually (rather than via benchmark.convergence_curve) so
|
|
94
|
+
# each N's sample can be disk-cached individually -- this is the
|
|
95
|
+
# single slowest test in the suite (GinSE's dense 2n x 2n eigensolve
|
|
96
|
+
# dominates), so caching it matters most here.
|
|
97
|
+
benchmark = rmt.validation.CircularLaw()
|
|
98
|
+
n_values = [40, 90, 200, 400]
|
|
99
|
+
results = []
|
|
100
|
+
for n in n_values:
|
|
101
|
+
ens = cls(n=n, seed=17)
|
|
102
|
+
spectrum = cached_sample(ens, n_samples=12)
|
|
103
|
+
results.append(benchmark.validate(spectrum, seed=17))
|
|
104
|
+
|
|
105
|
+
ks_stats = np.array([r.ks_statistic for r in results])
|
|
106
|
+
slope = linregress(np.log(n_values), np.log(ks_stats)).slope
|
|
107
|
+
# Genuinely shallower than the Hermitian ensembles' convergence
|
|
108
|
+
# (see module docstring) -- require a real negative trend, not the
|
|
109
|
+
# steeper threshold used for the semicircle/MP tests.
|
|
110
|
+
assert slope < -0.1, f"{name}: expected shrinking KS distance, slope={slope}"
|
|
111
|
+
assert ks_stats[-1] < 0.04
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_real_ginibre_has_real_eigenvalues_edelman_kostlan_shub():
|
|
115
|
+
# Edelman, Kostlan, Shub (1994): E[# real eigenvalues] ~ sqrt(2n/pi).
|
|
116
|
+
# Checked as an order-of-magnitude sanity check, not a tight
|
|
117
|
+
# quantitative match (this is an expectation over the ensemble, and
|
|
118
|
+
# we only average over a modest number of samples here).
|
|
119
|
+
n = 500
|
|
120
|
+
ens = rmt.ensembles.GinOE(n=n, seed=5)
|
|
121
|
+
spectrum = cached_sample(ens, n_samples=15)
|
|
122
|
+
real_counts = np.sum(np.abs(spectrum.eigenvalues.imag) < 1e-8, axis=1)
|
|
123
|
+
expected = np.sqrt(2 * n / np.pi)
|
|
124
|
+
assert real_counts.mean() == pytest.approx(expected, rel=0.5)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_complex_ginibre_has_no_real_eigenvalues_generically():
|
|
128
|
+
ens = rmt.ensembles.GinUE(n=200, seed=6)
|
|
129
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
130
|
+
real_fraction = np.mean(np.abs(spectrum.eigenvalues.imag) < 1e-8)
|
|
131
|
+
assert real_fraction < 0.02 # essentially none, unlike GinOE
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Tests for the Girko ensembles (IIDEnsemble, GirkoElliptic) --
|
|
2
|
+
universality generalizations of the Ginibre ensembles: the circular and
|
|
3
|
+
elliptic laws hold for any i.i.d. entry distribution with the right
|
|
4
|
+
first two moments, not just Gaussian.
|
|
5
|
+
|
|
6
|
+
IIDEnsemble is validated against the SAME ``CircularLaw`` benchmark
|
|
7
|
+
already used for Ginibre in ``tests/test_ginibre_circular_law.py`` --
|
|
8
|
+
that reuse is itself the point: Girko's theorem says non-Gaussian iid
|
|
9
|
+
matrices obey the identical limiting law, so no new theoretical machinery
|
|
10
|
+
is needed. GirkoElliptic has no existing benchmark (the general 2-D
|
|
11
|
+
elliptic density isn't wired into the KS/Wasserstein ``Benchmark`` base
|
|
12
|
+
class), so it is checked via its known exact structural limits
|
|
13
|
+
(rho=+-1) and the known ellipse semi-axes (1+rho, 1-rho) at general rho.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import pytest
|
|
18
|
+
from cache_utils import cached_sample
|
|
19
|
+
from scipy.stats import linregress
|
|
20
|
+
|
|
21
|
+
import physicskit.rmt as rmt
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_iid_ensemble_beta_is_none():
|
|
25
|
+
ens = rmt.ensembles.IIDEnsemble(n=30, entry_sampler=rmt.ensembles.rademacher, seed=0)
|
|
26
|
+
assert ens.beta is None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_iid_ensemble_real_entries_branch_produces_finite_eigenvalues():
|
|
30
|
+
# A fresh (uncached) direct .sample() call, to actually exercise the
|
|
31
|
+
# complex_entries=False code path rather than risk a disk-cache hit
|
|
32
|
+
# from a previous run with these same parameters.
|
|
33
|
+
ens = rmt.ensembles.IIDEnsemble(n=8, entry_sampler=rmt.ensembles.rademacher, complex_entries=False, seed=99)
|
|
34
|
+
spectrum = ens.sample(n_samples=2)
|
|
35
|
+
assert np.all(np.isfinite(spectrum.eigenvalues))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize(
|
|
39
|
+
"name,entry_sampler,complex_entries",
|
|
40
|
+
[
|
|
41
|
+
("uniform-complex", rmt.ensembles.uniform_unit_variance, True),
|
|
42
|
+
("rademacher-complex", rmt.ensembles.rademacher, True),
|
|
43
|
+
("exponential-real", rmt.ensembles.exponential_centered_unit_variance, False),
|
|
44
|
+
],
|
|
45
|
+
)
|
|
46
|
+
def test_iid_ensemble_eigenvalues_are_complex(name, entry_sampler, complex_entries):
|
|
47
|
+
ens = rmt.ensembles.IIDEnsemble(
|
|
48
|
+
n=50,
|
|
49
|
+
entry_sampler=entry_sampler,
|
|
50
|
+
complex_entries=complex_entries,
|
|
51
|
+
seed=1,
|
|
52
|
+
)
|
|
53
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
54
|
+
assert np.iscomplexobj(spectrum.eigenvalues)
|
|
55
|
+
assert spectrum.eigenvalues.shape == (5, 50)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_iid_ensemble_reproducibility():
|
|
59
|
+
ens_a = rmt.ensembles.IIDEnsemble(n=40, entry_sampler=rmt.ensembles.rademacher, seed=42)
|
|
60
|
+
ens_b = rmt.ensembles.IIDEnsemble(n=40, entry_sampler=rmt.ensembles.rademacher, seed=42)
|
|
61
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
62
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
63
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@pytest.mark.parametrize(
|
|
67
|
+
"name,entry_sampler,complex_entries,seed",
|
|
68
|
+
[
|
|
69
|
+
("uniform", rmt.ensembles.uniform_unit_variance, True, 10),
|
|
70
|
+
("rademacher", rmt.ensembles.rademacher, True, 11),
|
|
71
|
+
("exponential", rmt.ensembles.exponential_centered_unit_variance, True, 12),
|
|
72
|
+
],
|
|
73
|
+
)
|
|
74
|
+
def test_circular_law_convergence_shrinks_with_n_non_gaussian(name, entry_sampler, complex_entries, seed):
|
|
75
|
+
# Same benchmark, same convergence-rate philosophy, and (per the
|
|
76
|
+
# module docstring) the same expected shallower log-log slope as
|
|
77
|
+
# Ginibre's own non-Hermitian convergence -- as this is meant to be
|
|
78
|
+
# the identical limiting law reached from a non-Gaussian starting
|
|
79
|
+
# point, not a distinct, looser standard.
|
|
80
|
+
benchmark = rmt.validation.CircularLaw()
|
|
81
|
+
n_values = [40, 90, 200, 400]
|
|
82
|
+
results = []
|
|
83
|
+
for n in n_values:
|
|
84
|
+
ens = rmt.ensembles.IIDEnsemble(
|
|
85
|
+
n=n,
|
|
86
|
+
entry_sampler=entry_sampler,
|
|
87
|
+
complex_entries=complex_entries,
|
|
88
|
+
seed=seed,
|
|
89
|
+
)
|
|
90
|
+
spectrum = cached_sample(ens, n_samples=12)
|
|
91
|
+
results.append(benchmark.validate(spectrum, seed=seed))
|
|
92
|
+
|
|
93
|
+
ks_stats = np.array([r.ks_statistic for r in results])
|
|
94
|
+
slope = linregress(np.log(n_values), np.log(ks_stats)).slope
|
|
95
|
+
assert slope < -0.1, f"{name}: expected shrinking KS distance, slope={slope}"
|
|
96
|
+
assert ks_stats[-1] < 0.04
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_girko_elliptic_rho_out_of_range_raises():
|
|
100
|
+
with pytest.raises(ValueError):
|
|
101
|
+
rmt.ensembles.GirkoElliptic(n=10, rho=1.5, seed=0)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def test_girko_elliptic_beta_is_none():
|
|
105
|
+
ens = rmt.ensembles.GirkoElliptic(n=20, rho=0.5, seed=0)
|
|
106
|
+
assert ens.beta is None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_girko_elliptic_reproducibility():
|
|
110
|
+
ens_a = rmt.ensembles.GirkoElliptic(n=40, rho=0.3, seed=42)
|
|
111
|
+
ens_b = rmt.ensembles.GirkoElliptic(n=40, rho=0.3, seed=42)
|
|
112
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
113
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
114
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_girko_elliptic_rho_zero_matches_circular_law():
|
|
118
|
+
# rho=0 -> plain i.i.d. real entries -> same limiting law as
|
|
119
|
+
# IIDEnsemble(complex_entries=False) / GinOE (circular law).
|
|
120
|
+
benchmark = rmt.validation.CircularLaw()
|
|
121
|
+
ens = rmt.ensembles.GirkoElliptic(n=300, rho=0.0, seed=2)
|
|
122
|
+
spectrum = cached_sample(ens, n_samples=15)
|
|
123
|
+
result = benchmark.validate(spectrum, seed=2)
|
|
124
|
+
assert result.ks_statistic < 0.05
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_girko_elliptic_rho_plus_one_gives_exactly_real_eigenvalues():
|
|
128
|
+
# rho=1 -> X exactly symmetric -> eigenvalues exactly real (Wigner).
|
|
129
|
+
ens = rmt.ensembles.GirkoElliptic(n=60, rho=1.0, seed=3)
|
|
130
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
131
|
+
assert np.abs(spectrum.eigenvalues.imag).max() < 1e-8
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_girko_elliptic_rho_minus_one_gives_exactly_imaginary_eigenvalues():
|
|
135
|
+
# rho=-1 -> X exactly antisymmetric -> eigenvalues exactly purely
|
|
136
|
+
# imaginary (real antisymmetric matrices have eigenvalues 0 or
|
|
137
|
+
# imaginary conjugate pairs).
|
|
138
|
+
ens = rmt.ensembles.GirkoElliptic(n=60, rho=-1.0, seed=4)
|
|
139
|
+
spectrum = cached_sample(ens, n_samples=5)
|
|
140
|
+
assert np.abs(spectrum.eigenvalues.real).max() < 1e-8
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def test_correlated_pair_matrix_has_correct_variance_and_correlation():
|
|
144
|
+
# Direct, un-cached check of the raw construction's second moments
|
|
145
|
+
# (not just the downstream eigenvalue law) against the values
|
|
146
|
+
# asserted in the module docstring, pooled over many independent
|
|
147
|
+
# matrices for statistical accuracy.
|
|
148
|
+
from physicskit.rmt.ensembles.girko import _correlated_pair_matrix, standard_normal
|
|
149
|
+
|
|
150
|
+
rng = np.random.default_rng(5)
|
|
151
|
+
n = 8
|
|
152
|
+
rho = 0.6
|
|
153
|
+
upper_vals, lower_vals = [], []
|
|
154
|
+
for _ in range(4000):
|
|
155
|
+
x = _correlated_pair_matrix(n, rho, standard_normal, rng)
|
|
156
|
+
upper_vals.append(x[0, 1])
|
|
157
|
+
lower_vals.append(x[1, 0])
|
|
158
|
+
upper_vals = np.array(upper_vals)
|
|
159
|
+
lower_vals = np.array(lower_vals)
|
|
160
|
+
assert upper_vals.var() == pytest.approx(1.0, abs=0.1)
|
|
161
|
+
assert lower_vals.var() == pytest.approx(1.0, abs=0.1)
|
|
162
|
+
corr = np.corrcoef(upper_vals, lower_vals)[0, 1]
|
|
163
|
+
assert corr == pytest.approx(rho, abs=0.05)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@pytest.mark.parametrize("rho", [-0.5, 0.0, 0.5, 0.8])
|
|
167
|
+
def test_girko_elliptic_semi_axes_bound_the_spectrum_at_large_n(rho):
|
|
168
|
+
# Eigenvalues should (asymptotically) stay within the ellipse of
|
|
169
|
+
# semi-axes (1+rho, 1-rho); allow a modest finite-n margin rather
|
|
170
|
+
# than a tight asymptotic bound.
|
|
171
|
+
n = 800
|
|
172
|
+
ens = rmt.ensembles.GirkoElliptic(n=n, rho=rho, seed=6)
|
|
173
|
+
spectrum = cached_sample(ens, n_samples=3)
|
|
174
|
+
z = spectrum.rescaled.ravel()
|
|
175
|
+
a, b = 1.0 + rho, max(1.0 - rho, 1e-6)
|
|
176
|
+
elliptic_radius = np.sqrt((z.real / a) ** 2 + (z.imag / b) ** 2)
|
|
177
|
+
frac_outside = np.mean(elliptic_radius > 1.15)
|
|
178
|
+
assert frac_outside < 0.03, f"rho={rho}: {frac_outside:.3f} of mass outside the ellipse"
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Tests for GraphLaplacianEnsemble (Erdos-Renyi combinatorial and
|
|
2
|
+
normalized Laplacians)."""
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pytest
|
|
6
|
+
from cache_utils import cached_sample
|
|
7
|
+
|
|
8
|
+
import physicskit.rmt as rmt
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_beta_is_none():
|
|
12
|
+
ens = rmt.ensembles.GraphLaplacianEnsemble(n=30, p=0.3, seed=0)
|
|
13
|
+
assert ens.beta is None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_invalid_p_rejected():
|
|
17
|
+
with pytest.raises(ValueError):
|
|
18
|
+
rmt.ensembles.GraphLaplacianEnsemble(n=10, p=0.0, seed=0)
|
|
19
|
+
with pytest.raises(ValueError):
|
|
20
|
+
rmt.ensembles.GraphLaplacianEnsemble(n=10, p=1.5, seed=0)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_normalized_laplacian_fresh_sample_produces_finite_eigenvalues():
|
|
24
|
+
# A fresh (uncached) direct .sample() call, to actually exercise the
|
|
25
|
+
# normalized=True code path rather than risk a disk-cache hit from a
|
|
26
|
+
# previous run with these same parameters.
|
|
27
|
+
ens = rmt.ensembles.GraphLaplacianEnsemble(n=15, p=0.5, normalized=True, seed=123)
|
|
28
|
+
spectrum = ens.sample(n_samples=2)
|
|
29
|
+
assert np.all(np.isfinite(spectrum.eigenvalues))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_reproducibility():
|
|
33
|
+
ens_a = rmt.ensembles.GraphLaplacianEnsemble(n=40, p=0.2, seed=42)
|
|
34
|
+
ens_b = rmt.ensembles.GraphLaplacianEnsemble(n=40, p=0.2, seed=42)
|
|
35
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
36
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
37
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_combinatorial_laplacian_is_positive_semidefinite_with_zero_mode():
|
|
41
|
+
# L = D - A is always PSD with smallest eigenvalue exactly 0 (the
|
|
42
|
+
# constant vector is always in its kernel, connected or not).
|
|
43
|
+
ens = rmt.ensembles.GraphLaplacianEnsemble(n=50, p=0.3, seed=1)
|
|
44
|
+
spectrum = cached_sample(ens, n_samples=10)
|
|
45
|
+
assert np.all(spectrum.eigenvalues >= -1e-8)
|
|
46
|
+
assert np.abs(spectrum.eigenvalues.min(axis=1)).max() < 1e-8
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_normalized_laplacian_eigenvalues_bounded_in_zero_two():
|
|
50
|
+
# Exact graph-independent bound (Chung 1997), checked directly.
|
|
51
|
+
ens = rmt.ensembles.GraphLaplacianEnsemble(n=60, p=0.4, normalized=True, seed=2)
|
|
52
|
+
spectrum = cached_sample(ens, n_samples=10)
|
|
53
|
+
assert np.all(spectrum.eigenvalues >= -1e-8)
|
|
54
|
+
assert np.all(spectrum.eigenvalues <= 2.0 + 1e-8)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_normalized_laplacian_handles_isolated_vertices():
|
|
58
|
+
# Very small p on a modest n makes isolated vertices likely; should
|
|
59
|
+
# not raise (division-by-zero for degree-0 rows is handled by
|
|
60
|
+
# convention -- see module docstring) and eigenvalues must stay
|
|
61
|
+
# finite and within [0, 2].
|
|
62
|
+
ens = rmt.ensembles.GraphLaplacianEnsemble(n=30, p=0.02, normalized=True, seed=3)
|
|
63
|
+
spectrum = cached_sample(ens, n_samples=10)
|
|
64
|
+
assert np.all(np.isfinite(spectrum.eigenvalues))
|
|
65
|
+
assert np.all(spectrum.eigenvalues >= -1e-8)
|
|
66
|
+
assert np.all(spectrum.eigenvalues <= 2.0 + 1e-8)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_combinatorial_laplacian_zero_multiplicity_matches_component_count():
|
|
70
|
+
# Well-known spectral graph theory fact: the multiplicity of the
|
|
71
|
+
# zero eigenvalue equals the number of connected components. Force
|
|
72
|
+
# a disconnected graph (very small p, small n) and check directly
|
|
73
|
+
# via a graph-free connectivity computation (BFS/union-find on the
|
|
74
|
+
# same adjacency matrix used to build L).
|
|
75
|
+
from physicskit.rmt.ensembles.graph_laplacian import _erdos_renyi_adjacency
|
|
76
|
+
|
|
77
|
+
rng = np.random.default_rng(4)
|
|
78
|
+
n, p = 25, 0.03
|
|
79
|
+
a = _erdos_renyi_adjacency(n, p, rng)
|
|
80
|
+
degree = a.sum(axis=1)
|
|
81
|
+
laplacian = np.diag(degree) - a
|
|
82
|
+
eigs = np.linalg.eigvalsh(laplacian)
|
|
83
|
+
zero_multiplicity = np.sum(np.abs(eigs) < 1e-8)
|
|
84
|
+
|
|
85
|
+
# union-find to count connected components independently
|
|
86
|
+
parent = list(range(n))
|
|
87
|
+
|
|
88
|
+
def find(x):
|
|
89
|
+
while parent[x] != x:
|
|
90
|
+
parent[x] = parent[parent[x]]
|
|
91
|
+
x = parent[x]
|
|
92
|
+
return x
|
|
93
|
+
|
|
94
|
+
for i in range(n):
|
|
95
|
+
for j in range(i + 1, n):
|
|
96
|
+
if a[i, j] > 0:
|
|
97
|
+
ri, rj = find(i), find(j)
|
|
98
|
+
if ri != rj:
|
|
99
|
+
parent[ri] = rj
|
|
100
|
+
n_components = len({find(i) for i in range(n)})
|
|
101
|
+
|
|
102
|
+
assert zero_multiplicity == n_components
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Tests for the Haar-random matrix generators (physicskit.rmt.utils.haar) and
|
|
2
|
+
the HaarOrthogonalEnsemble.
|
|
3
|
+
|
|
4
|
+
``haar_unitary`` is already indirectly validated via CUE's passing
|
|
5
|
+
spacing statistics (see tests/test_circular.py); ``haar_orthogonal`` and
|
|
6
|
+
``haar_symplectic`` are checked directly here since they are new.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pytest
|
|
11
|
+
from cache_utils import cached_sample
|
|
12
|
+
|
|
13
|
+
import physicskit.rmt as rmt
|
|
14
|
+
from physicskit.rmt.utils.haar import haar_orthogonal, haar_symplectic, haar_unitary, symplectic_form
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_haar_orthogonal_is_orthogonal():
|
|
18
|
+
rng = np.random.default_rng(0)
|
|
19
|
+
o = haar_orthogonal(20, rng)
|
|
20
|
+
assert np.abs(o.T @ o - np.eye(20)).max() < 1e-10
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.slow
|
|
24
|
+
def test_haar_orthogonal_first_column_is_uniform_on_sphere():
|
|
25
|
+
# Hallmark of Haar invariance: any fixed unit vector rotated by a
|
|
26
|
+
# Haar-random O(n) is uniform on the sphere -- mean 0, second
|
|
27
|
+
# moment 1/n per component.
|
|
28
|
+
rng = np.random.default_rng(1)
|
|
29
|
+
n = 5
|
|
30
|
+
first_columns = np.array([haar_orthogonal(n, rng)[:, 0] for _ in range(20_000)])
|
|
31
|
+
np.testing.assert_allclose(first_columns.mean(axis=0), 0.0, atol=0.03)
|
|
32
|
+
np.testing.assert_allclose((first_columns**2).mean(axis=0), 1.0 / n, atol=0.03)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_haar_orthogonal_determinant_is_balanced():
|
|
36
|
+
rng = np.random.default_rng(2)
|
|
37
|
+
dets = [np.linalg.det(haar_orthogonal(6, rng)) for _ in range(4000)]
|
|
38
|
+
dets = np.array(dets)
|
|
39
|
+
np.testing.assert_allclose(np.abs(dets), 1.0, atol=1e-8)
|
|
40
|
+
frac_positive = np.mean(dets > 0)
|
|
41
|
+
assert frac_positive == pytest.approx(0.5, abs=0.05)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_haar_symplectic_is_unitary_and_self_dual():
|
|
45
|
+
rng = np.random.default_rng(3)
|
|
46
|
+
n_quaternionic = 6
|
|
47
|
+
u = haar_symplectic(n_quaternionic, rng)
|
|
48
|
+
n = 2 * n_quaternionic
|
|
49
|
+
assert u.shape == (n, n)
|
|
50
|
+
assert np.abs(u.conj().T @ u - np.eye(n)).max() < 1e-10
|
|
51
|
+
|
|
52
|
+
z = symplectic_form(n)
|
|
53
|
+
u_dual = (-z) @ u.T @ z
|
|
54
|
+
assert np.abs(u_dual - u).max() < 1e-10
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_haar_symplectic_eigenvalues_are_doubly_degenerate():
|
|
58
|
+
rng = np.random.default_rng(4)
|
|
59
|
+
u = haar_symplectic(5, rng)
|
|
60
|
+
eigs = np.linalg.eigvals(u)
|
|
61
|
+
order = np.lexsort((eigs.imag, eigs.real))
|
|
62
|
+
sorted_eigs = eigs[order]
|
|
63
|
+
pair_diffs = np.abs(sorted_eigs[0::2] - sorted_eigs[1::2])
|
|
64
|
+
assert pair_diffs.max() < 1e-8
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_haar_unitary_still_exactly_unitary():
|
|
68
|
+
# Regression check: existing helper unaffected by the new additions.
|
|
69
|
+
rng = np.random.default_rng(5)
|
|
70
|
+
u = haar_unitary(10, rng)
|
|
71
|
+
assert np.abs(u.conj().T @ u - np.eye(10)).max() < 1e-10
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# --- HaarOrthogonalEnsemble ---
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_haar_orthogonal_ensemble_beta_is_none():
|
|
78
|
+
ens = rmt.ensembles.HaarOrthogonalEnsemble(n=20, seed=0)
|
|
79
|
+
assert ens.beta is None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_haar_orthogonal_ensemble_eigenvalues_on_unit_circle():
|
|
83
|
+
ens = rmt.ensembles.HaarOrthogonalEnsemble(n=30, seed=1)
|
|
84
|
+
spectrum = cached_sample(ens, n_samples=10)
|
|
85
|
+
radii = np.abs(spectrum.eigenvalues)
|
|
86
|
+
np.testing.assert_allclose(radii, 1.0, atol=1e-8)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_haar_orthogonal_ensemble_reproducibility():
|
|
90
|
+
ens_a = rmt.ensembles.HaarOrthogonalEnsemble(n=20, seed=42)
|
|
91
|
+
ens_b = rmt.ensembles.HaarOrthogonalEnsemble(n=20, seed=42)
|
|
92
|
+
spec_a = ens_a.sample(n_samples=3)
|
|
93
|
+
spec_b = ens_b.sample(n_samples=3)
|
|
94
|
+
np.testing.assert_allclose(spec_a.eigenvalues, spec_b.eigenvalues)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_haar_orthogonal_ensemble_has_real_eigenvalues_unlike_coe():
|
|
98
|
+
# The core structural distinction from COE (module docstring):
|
|
99
|
+
# a raw Haar-O(n) matrix genuinely has real (+-1) eigenvalues with
|
|
100
|
+
# non-negligible probability; COE/CUE generically do not.
|
|
101
|
+
ens = rmt.ensembles.HaarOrthogonalEnsemble(n=40, seed=2)
|
|
102
|
+
spectrum = cached_sample(ens, n_samples=200)
|
|
103
|
+
real_fraction_of_samples = np.mean(np.any(np.abs(spectrum.eigenvalues.imag) < 1e-8, axis=1))
|
|
104
|
+
assert real_fraction_of_samples > 0.5
|
|
105
|
+
|
|
106
|
+
# COE stores eigenvalue PHASES (theta in [0, 2*pi)), not e^{i*theta}
|
|
107
|
+
# directly -- convert back to compare on the same "is this real"
|
|
108
|
+
# footing as HaarOrthogonalEnsemble's complex output above.
|
|
109
|
+
coe = rmt.ensembles.COE(n=40, seed=3)
|
|
110
|
+
coe_spectrum = cached_sample(coe, n_samples=200)
|
|
111
|
+
coe_eigs = np.exp(1j * coe_spectrum.eigenvalues)
|
|
112
|
+
coe_real_fraction = np.mean(np.abs(coe_eigs.imag) < 1e-8)
|
|
113
|
+
assert coe_real_fraction < 0.01
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_symplectic_form_rejects_odd_dimension():
|
|
117
|
+
with pytest.raises(ValueError):
|
|
118
|
+
symplectic_form(3)
|