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,207 @@
|
|
|
1
|
+
"""Embedded Gaussian ensembles (EGOE(k)/EGUE(k)) and the Two-Body Random
|
|
2
|
+
Ensemble (TBRE, k=2) -- random k-body interactions among m fermions
|
|
3
|
+
distributed over N single-particle levels.
|
|
4
|
+
|
|
5
|
+
References
|
|
6
|
+
----------
|
|
7
|
+
K. K. Mon, J. B. French, "Statistical properties of many-particle
|
|
8
|
+
spectra", Ann. Phys. 95 (1975) 90 -- the k-body embedded ensemble
|
|
9
|
+
construction and the celebrated result that the many-body density of
|
|
10
|
+
states approaches a GAUSSIAN (not the semicircle law) as m, N -> infinity
|
|
11
|
+
at fixed k >= 2, k < m.
|
|
12
|
+
T. A. Brody, J. Flores, J. B. French, P. A. Mello, A. Pandey,
|
|
13
|
+
S. S. M. Wong, "Random-matrix physics: spectrum and strength
|
|
14
|
+
fluctuations", Rev. Mod. Phys. 53 (1981) 385 -- review.
|
|
15
|
+
V. K. B. Kota, "Embedded Random Matrix Ensembles in Quantum Physics",
|
|
16
|
+
Lecture Notes in Physics 884, Springer, 2014 -- comprehensive reference.
|
|
17
|
+
T. Papenbrock, H. A. Weidenmuller, "Colloquium: Random matrices and
|
|
18
|
+
chaos in nuclear physics", Rev. Mod. Phys. 79 (2007) 997.
|
|
19
|
+
|
|
20
|
+
Unlike every classical ensemble in this package (i.i.d. matrix entries),
|
|
21
|
+
the many-body Hamiltonian matrix here is built by EMBEDDING a random
|
|
22
|
+
k-body interaction into the m-particle Fock space: the interaction
|
|
23
|
+
itself is a Hermitian random (GOE/GUE-type) matrix V on the space of
|
|
24
|
+
k-particle Slater determinants (dimension C(N, k)), and the m-particle
|
|
25
|
+
Hamiltonian is
|
|
26
|
+
|
|
27
|
+
H = sum_{I, J} V_{I, J} * a^dagger_I a_J
|
|
28
|
+
|
|
29
|
+
summed over all k-index tuples I, J (I = i_1 < ... < i_k, similarly J),
|
|
30
|
+
with a^dagger_I = a^dagger_{i_1} ... a^dagger_{i_k} and
|
|
31
|
+
a_J = a_{j_k} ... a_{j_1} (reversed order, so a^dagger_I a_J is built
|
|
32
|
+
from creation operators applied in ASCENDING index order and
|
|
33
|
+
annihilation operators in DESCENDING index order -- get this backwards
|
|
34
|
+
and Hermiticity silently survives but the exact k=m identity below does
|
|
35
|
+
not, which is exactly how an initial, subtly wrong sign convention
|
|
36
|
+
during development was caught).
|
|
37
|
+
|
|
38
|
+
Because H is built from a SHARED set of k-body matrix elements rather
|
|
39
|
+
than independent entries, the resulting many-body matrix elements are
|
|
40
|
+
strongly correlated (unlike GOE) -- this correlation structure is
|
|
41
|
+
exactly what drives the many-body density of states toward a Gaussian
|
|
42
|
+
rather than a semicircle as m, N grow (Mon-French 1975), the qualitative
|
|
43
|
+
signature this ensemble exists to demonstrate.
|
|
44
|
+
|
|
45
|
+
Implementation: creation/annihilation operators are realized via the
|
|
46
|
+
standard Jordan-Wigner tensor-product construction over N qubits (one
|
|
47
|
+
per single-particle level, ``|0>``/``|1>`` = unoccupied/occupied) -- the SAME
|
|
48
|
+
technique already used for SYK's Majorana operators
|
|
49
|
+
(``physicskit.rmt.ensembles.syk``), just for fermionic number-conserving ladder
|
|
50
|
+
operators instead. H is built on the full 2^N Fock space, then restricted
|
|
51
|
+
to the fixed-m-particle sector (dimension C(N, m)) by selecting the
|
|
52
|
+
appropriate basis states -- exact, not an approximation, since
|
|
53
|
+
number-conserving k-body terms never mix different particle-number
|
|
54
|
+
sectors.
|
|
55
|
+
|
|
56
|
+
Verified during development (see ``tests/test_embedded.py``), independent
|
|
57
|
+
of trusting the construction's well-known status in the literature:
|
|
58
|
+
|
|
59
|
+
- H is exactly Hermitian and exactly commutes with the total particle
|
|
60
|
+
number operator (both to machine precision).
|
|
61
|
+
- The celebrated special case k = m: with only m particles present, a
|
|
62
|
+
k=m-body interaction acts on all of them at once, so the embedding is
|
|
63
|
+
trivial and the m-particle Hamiltonian reduces EXACTLY (not merely
|
|
64
|
+
statistically) to the k-body matrix V itself -- checked directly for
|
|
65
|
+
k=m=2 and k=m=3 to machine precision, catching the operator-ordering
|
|
66
|
+
sign bug mentioned above during development.
|
|
67
|
+
|
|
68
|
+
Cost note (genuinely different from the classical ensembles, exactly
|
|
69
|
+
like SYK): the Fock-space dimension is 2^N, and building H requires
|
|
70
|
+
O(C(N,k)^2) dense Hilbert-space operator products -- exponential in N,
|
|
71
|
+
not polynomial. Keep ``n_levels`` small (roughly <= 10) for interactive
|
|
72
|
+
use.
|
|
73
|
+
|
|
74
|
+
``beta`` in {1, 2} selects a real-symmetric (EGOE, "orthogonal") or
|
|
75
|
+
complex-Hermitian (EGUE, "unitary") random two-body interaction V; no
|
|
76
|
+
simple universal ``natural_scale`` rescaling is attempted (unlike GOE's
|
|
77
|
+
sqrt(n), the many-body spectral width here depends on N, m, and k
|
|
78
|
+
through a nontrivial combinatorial moment formula -- see Mon-French
|
|
79
|
+
1975 -- not implemented here).
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
from __future__ import annotations
|
|
83
|
+
|
|
84
|
+
from itertools import combinations
|
|
85
|
+
|
|
86
|
+
import numpy as np
|
|
87
|
+
|
|
88
|
+
from .base import MatrixEnsemble
|
|
89
|
+
|
|
90
|
+
_I2 = np.eye(2, dtype=complex)
|
|
91
|
+
_Z = np.array([[1.0, 0.0], [0.0, -1.0]], dtype=complex)
|
|
92
|
+
_CREATE_LOCAL = np.array([[0.0, 0.0], [1.0, 0.0]], dtype=complex) # |0>=unoccupied -> |1>=occupied
|
|
93
|
+
_ANNIHILATE_LOCAL = np.array([[0.0, 1.0], [0.0, 0.0]], dtype=complex)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _fermion_ladder_operators(n_levels: int) -> list[np.ndarray]:
|
|
97
|
+
"""The N fermionic creation operators a^dagger_1, ..., a^dagger_N as
|
|
98
|
+
dense 2^N x 2^N complex matrices (Jordan-Wigner construction; the
|
|
99
|
+
corresponding annihilation operators are their conjugate transposes).
|
|
100
|
+
"""
|
|
101
|
+
creators = []
|
|
102
|
+
for k in range(n_levels):
|
|
103
|
+
factors = [_Z] * k + [_CREATE_LOCAL] + [_I2] * (n_levels - k - 1)
|
|
104
|
+
op = factors[0]
|
|
105
|
+
for factor in factors[1:]:
|
|
106
|
+
op = np.kron(op, factor)
|
|
107
|
+
creators.append(op)
|
|
108
|
+
return creators
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class EmbeddedGaussianEnsemble(MatrixEnsemble):
|
|
112
|
+
"""EGOE(k)/EGUE(k): m fermions in N single-particle levels,
|
|
113
|
+
interacting via a random k-body interaction (see module docstring).
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
n_particles : int
|
|
118
|
+
Number of fermions m.
|
|
119
|
+
n_levels : int
|
|
120
|
+
Number of single-particle levels N (>= n_particles). Keep small
|
|
121
|
+
(roughly <= 10) -- see the module docstring's cost note.
|
|
122
|
+
k : int, optional
|
|
123
|
+
Interaction body-rank (default 2, i.e. TBRE). Must satisfy
|
|
124
|
+
2 <= k <= n_particles.
|
|
125
|
+
beta : int, optional
|
|
126
|
+
1 (real symmetric interaction, EGOE) or 2 (complex Hermitian,
|
|
127
|
+
EGUE).
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(
|
|
131
|
+
self,
|
|
132
|
+
n_particles: int,
|
|
133
|
+
n_levels: int,
|
|
134
|
+
k: int = 2,
|
|
135
|
+
beta: int = 1,
|
|
136
|
+
seed: int | np.random.Generator | None = None,
|
|
137
|
+
) -> None:
|
|
138
|
+
if beta not in (1, 2):
|
|
139
|
+
raise ValueError(f"beta must be 1 or 2, got {beta}")
|
|
140
|
+
if not 2 <= k <= n_particles <= n_levels:
|
|
141
|
+
raise ValueError(f"require 2 <= k <= n_particles <= n_levels; got k={k}, n_particles={n_particles}, n_levels={n_levels}")
|
|
142
|
+
super().__init__(n_particles, seed=seed)
|
|
143
|
+
self.n_levels = n_levels
|
|
144
|
+
self.k = k
|
|
145
|
+
self.beta: float = beta
|
|
146
|
+
self._p_ops: list[np.ndarray] | None = None
|
|
147
|
+
self._basis: list[int] | None = None
|
|
148
|
+
|
|
149
|
+
def _operators(self) -> tuple[list[np.ndarray], list[int]]:
|
|
150
|
+
"""The k-body creation operators P_a (one per k-subset of levels)
|
|
151
|
+
and the fixed-m-particle basis indices -- independent of the
|
|
152
|
+
random interaction V, so computed once and cached rather than
|
|
153
|
+
redone on every ``_sample_eigenvalues`` call.
|
|
154
|
+
"""
|
|
155
|
+
if self._p_ops is None:
|
|
156
|
+
n_levels, k, m = self.n_levels, self.k, self.n
|
|
157
|
+
creators = _fermion_ladder_operators(n_levels)
|
|
158
|
+
tuples = list(combinations(range(n_levels), k))
|
|
159
|
+
|
|
160
|
+
def multi_create(idxs: tuple[int, ...]) -> np.ndarray:
|
|
161
|
+
op = creators[idxs[0]]
|
|
162
|
+
for idx in idxs[1:]:
|
|
163
|
+
op = op @ creators[idx]
|
|
164
|
+
return op
|
|
165
|
+
|
|
166
|
+
self._p_ops = [multi_create(t) for t in tuples]
|
|
167
|
+
dim = self._p_ops[0].shape[0]
|
|
168
|
+
self._basis = [state for state in range(dim) if bin(state).count("1") == m]
|
|
169
|
+
return self._p_ops, self._basis
|
|
170
|
+
|
|
171
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
172
|
+
p_ops, basis = self._operators()
|
|
173
|
+
n_tuples = len(p_ops)
|
|
174
|
+
|
|
175
|
+
if self.beta == 1:
|
|
176
|
+
x = rng.standard_normal((n_tuples, n_tuples))
|
|
177
|
+
v = (x + x.T) / np.sqrt(2.0)
|
|
178
|
+
else:
|
|
179
|
+
x = (rng.standard_normal((n_tuples, n_tuples)) + 1j * rng.standard_normal((n_tuples, n_tuples))) / np.sqrt(2.0)
|
|
180
|
+
v = (x + x.conj().T) / 2.0
|
|
181
|
+
|
|
182
|
+
dim = p_ops[0].shape[0]
|
|
183
|
+
h = np.zeros((dim, dim), dtype=complex)
|
|
184
|
+
for a_idx in range(n_tuples):
|
|
185
|
+
q = sum(v[a_idx, b_idx] * p_ops[b_idx].conj().T for b_idx in range(n_tuples))
|
|
186
|
+
h += p_ops[a_idx] @ q
|
|
187
|
+
|
|
188
|
+
h_sub = h[np.ix_(basis, basis)]
|
|
189
|
+
h_sub = (h_sub + h_sub.conj().T) / 2.0
|
|
190
|
+
return np.linalg.eigvalsh(h_sub)
|
|
191
|
+
|
|
192
|
+
def natural_scale(self) -> float:
|
|
193
|
+
return 1.0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class TwoBodyRandomEnsemble(EmbeddedGaussianEnsemble):
|
|
197
|
+
"""TBRE: the k=2 special case of :class:`EmbeddedGaussianEnsemble`,
|
|
198
|
+
by far the most commonly studied (nuclear shell-model-type) case."""
|
|
199
|
+
|
|
200
|
+
def __init__(
|
|
201
|
+
self,
|
|
202
|
+
n_particles: int,
|
|
203
|
+
n_levels: int,
|
|
204
|
+
beta: int = 1,
|
|
205
|
+
seed: int | np.random.Generator | None = None,
|
|
206
|
+
) -> None:
|
|
207
|
+
super().__init__(n_particles, n_levels, k=2, beta=beta, seed=seed)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Gaussian (Hermite) ensembles -- GOE, GUE, GSE -- plus the continuum
|
|
2
|
+
Dyson-index generalization, all built on the Dumitriu-Edelman tridiagonal
|
|
3
|
+
model (see ``physicskit.rmt.utils.tridiagonal``).
|
|
4
|
+
|
|
5
|
+
References
|
|
6
|
+
----------
|
|
7
|
+
E. Wigner, Ann. Math. 62 (1955) 548; Ann. Math. 67 (1958) 325.
|
|
8
|
+
M. L. Mehta, "Random Matrices" (3rd ed.), Academic Press, 2004.
|
|
9
|
+
I. Dumitriu, A. Edelman, J. Math. Phys. 43, 5830 (2002).
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from ..utils.tridiagonal import sample_hermite_beta_eigenvalues
|
|
17
|
+
from .base import MatrixEnsemble
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _dense_hermite(n: int, beta: int, rng: np.random.Generator) -> np.ndarray:
|
|
21
|
+
"""Dense n x n GOE/GUE matrix (beta=1 real symmetric, beta=2 complex
|
|
22
|
+
Hermitian), same off-diagonal-variance-1/diagonal-variance-2
|
|
23
|
+
convention as the tridiagonal model above.
|
|
24
|
+
|
|
25
|
+
Used only for ``_sample_eigenvalues_and_vectors``: unlike the
|
|
26
|
+
tridiagonal model, whose eigenVALUES are exact in distribution but
|
|
27
|
+
whose eigenVECTORS are an artifact of that particular sparse
|
|
28
|
+
representation (verified numerically during development -- their
|
|
29
|
+
inverse participation ratio was 2-3x too large relative to the exact
|
|
30
|
+
Haar-vector result below, and the gap grew with n), a genuinely
|
|
31
|
+
dense, rotationally/unitarily-invariant construction is needed to
|
|
32
|
+
get Haar-distributed eigenvectors. See ``_dense_hermite_quaternion``
|
|
33
|
+
for the beta=4 (GSE) analogue.
|
|
34
|
+
"""
|
|
35
|
+
if beta == 1:
|
|
36
|
+
x = rng.standard_normal((n, n))
|
|
37
|
+
return (x + x.T) / np.sqrt(2.0)
|
|
38
|
+
x = (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
|
|
39
|
+
return (x + x.conj().T) / 2.0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _dense_hermite_quaternion(n: int, rng: np.random.Generator) -> np.ndarray:
|
|
43
|
+
"""Dense n x n quaternionic self-dual Hermitian matrix (GSE),
|
|
44
|
+
embedded as a 2n x 2n complex matrix via the standard
|
|
45
|
+
quaternion-to-2x2-complex-block map (same convention as
|
|
46
|
+
``physicskit.rmt.ensembles.ginibre.GinSE``).
|
|
47
|
+
|
|
48
|
+
Its 2n eigenvalues come in exact Kramers double-degenerate pairs
|
|
49
|
+
(like GSE/CSE/EffGSE/JSE); ``_sample_eigenvalues_and_vectors`` keeps
|
|
50
|
+
one representative eigenvalue AND its (2n-dimensional complex, i.e.
|
|
51
|
+
n-quaternion) eigenvector per pair. Verified numerically during
|
|
52
|
+
development that folding each such eigenvector into n per-site
|
|
53
|
+
quaternion weights ``|v[2i]|^2 + |v[2i+1]|^2`` gives an inverse
|
|
54
|
+
participation ratio matching the exact beta=4 Haar-vector theory
|
|
55
|
+
(``physicskit.rmt.stats.localization.ipr_theory(n, beta=4)``) to four
|
|
56
|
+
significant figures -- see
|
|
57
|
+
``physicskit.rmt.stats.localization.inverse_participation_ratio_quaternionic``
|
|
58
|
+
and ``tests/test_localization.py``.
|
|
59
|
+
"""
|
|
60
|
+
a = rng.standard_normal((n, n))
|
|
61
|
+
b = rng.standard_normal((n, n))
|
|
62
|
+
c = rng.standard_normal((n, n))
|
|
63
|
+
d = rng.standard_normal((n, n))
|
|
64
|
+
m = np.zeros((2 * n, 2 * n), dtype=complex)
|
|
65
|
+
m[0::2, 0::2] = a + 1j * b
|
|
66
|
+
m[0::2, 1::2] = c + 1j * d
|
|
67
|
+
m[1::2, 0::2] = -c + 1j * d
|
|
68
|
+
m[1::2, 1::2] = a - 1j * b
|
|
69
|
+
return (m + m.conj().T) / 2.0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class HermiteBetaEnsemble(MatrixEnsemble):
|
|
73
|
+
"""General beta-Hermite (Gaussian) ensemble.
|
|
74
|
+
|
|
75
|
+
beta=1, 2, 4 recover GOE, GUE, GSE exactly (same joint eigenvalue
|
|
76
|
+
density, not merely the same limiting law); any beta > 0 is a valid
|
|
77
|
+
continuum generalization, useful for studying the Dyson index as a
|
|
78
|
+
continuous "inverse temperature" parameter.
|
|
79
|
+
|
|
80
|
+
Use the named subclasses (:class:`GOE`, :class:`GUE`, :class:`GSE`)
|
|
81
|
+
for the classical cases; use this class directly for other beta.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
def __init__(self, n: int, beta: float, seed: int | np.random.Generator | None = None) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
n : int
|
|
89
|
+
Matrix dimension.
|
|
90
|
+
beta : float
|
|
91
|
+
Dyson index; must be positive.
|
|
92
|
+
seed : int, numpy.random.Generator, or None, optional
|
|
93
|
+
"""
|
|
94
|
+
if beta <= 0:
|
|
95
|
+
raise ValueError(f"beta must be positive, got {beta}")
|
|
96
|
+
super().__init__(n, seed=seed)
|
|
97
|
+
self.beta: float = float(beta)
|
|
98
|
+
|
|
99
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
100
|
+
return sample_hermite_beta_eigenvalues(self.n, self.beta, rng)
|
|
101
|
+
|
|
102
|
+
def _sample_eigenvalues_and_vectors(self, rng: np.random.Generator) -> tuple[np.ndarray, np.ndarray]:
|
|
103
|
+
if self.beta not in (1, 2, 4):
|
|
104
|
+
raise NotImplementedError("return_eigenvectors=True is only supported for beta in (1, 2, 4) (GOE, GUE, GSE).")
|
|
105
|
+
if self.beta == 4:
|
|
106
|
+
h = _dense_hermite_quaternion(self.n, rng)
|
|
107
|
+
eigenvalues, eigenvectors = np.linalg.eigh(h)
|
|
108
|
+
# Exact Kramers double degeneracy: keep one eigenvalue and its
|
|
109
|
+
# (2n-dim complex) eigenvector per pair -- see
|
|
110
|
+
# _dense_hermite_quaternion's docstring.
|
|
111
|
+
return eigenvalues[0::2], eigenvectors[:, 0::2]
|
|
112
|
+
h = _dense_hermite(self.n, int(self.beta), rng)
|
|
113
|
+
eigenvalues, eigenvectors = np.linalg.eigh(h)
|
|
114
|
+
return eigenvalues, eigenvectors
|
|
115
|
+
|
|
116
|
+
def natural_scale(self) -> float:
|
|
117
|
+
# Verified empirically (see design notes): raw eigenvalue spread
|
|
118
|
+
# grows as sqrt(n * beta); dividing by this recovers the standard
|
|
119
|
+
# semicircle normalization with support [-2, 2] for all beta.
|
|
120
|
+
return np.sqrt(self.n * self.beta)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class GOE(HermiteBetaEnsemble):
|
|
124
|
+
"""Gaussian Orthogonal Ensemble (beta=1).
|
|
125
|
+
|
|
126
|
+
Real symmetric matrices; time-reversal symmetric with T^2 = +1.
|
|
127
|
+
Altland-Zirnbauer class AI.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, n: int, seed: int | np.random.Generator | None = None) -> None:
|
|
131
|
+
super().__init__(n, beta=1, seed=seed)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class GUE(HermiteBetaEnsemble):
|
|
135
|
+
"""Gaussian Unitary Ensemble (beta=2).
|
|
136
|
+
|
|
137
|
+
Complex Hermitian matrices; no time-reversal symmetry.
|
|
138
|
+
Altland-Zirnbauer class A.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, n: int, seed: int | np.random.Generator | None = None) -> None:
|
|
142
|
+
super().__init__(n, beta=2, seed=seed)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class GSE(HermiteBetaEnsemble):
|
|
146
|
+
"""Gaussian Symplectic Ensemble (beta=4).
|
|
147
|
+
|
|
148
|
+
Quaternionic self-dual Hermitian matrices; time-reversal symmetric
|
|
149
|
+
with T^2 = -1. Altland-Zirnbauer class AII.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
def __init__(self, n: int, seed: int | np.random.Generator | None = None) -> None:
|
|
153
|
+
super().__init__(n, beta=4, seed=seed)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Ginibre ensembles -- GinOE (real), GinUE (complex), GinSE (quaternion).
|
|
2
|
+
|
|
3
|
+
References
|
|
4
|
+
----------
|
|
5
|
+
J. Ginibre, "Statistical Ensembles of Complex, Quaternion, and Real
|
|
6
|
+
Matrices", J. Math. Phys. 6 (1965) 440.
|
|
7
|
+
A. Edelman, E. Kostlan, S. Shub, "How many eigenvalues of a random
|
|
8
|
+
matrix are real?", J. Amer. Math. Soc. 7 (1994) 247 -- the real-Ginibre
|
|
9
|
+
real-eigenvalue-count result, validated (against its large-n asymptotic
|
|
10
|
+
form, not the exact finite-n closed form -- see
|
|
11
|
+
``physicskit.rmt.stats.real_ginibre`` for why) by
|
|
12
|
+
``physicskit.rmt.validation.RealGinibreEigenvalueCount``.
|
|
13
|
+
|
|
14
|
+
Unlike the Hermitian/unitary ensembles built so far, Ginibre matrices
|
|
15
|
+
have NO symmetry constraint at all -- just i.i.d. entries -- so their
|
|
16
|
+
eigenvalues are genuinely complex and spread over a 2-D region (the unit
|
|
17
|
+
disk, asymptotically), not confined to the real line or unit circle.
|
|
18
|
+
``Spectrum.eigenvalues`` for these ensembles is therefore a complex
|
|
19
|
+
array; ``natural_scale`` (sqrt(n)) rescales eigenvalues onto the unit
|
|
20
|
+
disk (Ginibre's exact result, not merely asymptotic, for finite n up to
|
|
21
|
+
this fixed rescaling).
|
|
22
|
+
|
|
23
|
+
Real vs. complex vs. quaternion Ginibre are NOT interchangeable dtypes
|
|
24
|
+
of the same construction -- they have qualitatively different eigenvalue
|
|
25
|
+
structure::
|
|
26
|
+
|
|
27
|
+
GinUE (complex): eigenvalues spread generically over the disk.
|
|
28
|
+
GinOE (real): a mix of purely real eigenvalues and complex-conjugate
|
|
29
|
+
pairs (the number of real eigenvalues grows like sqrt(2n/pi),
|
|
30
|
+
Edelman-Kostlan-Shub 1994) -- a vanishing fraction of n as
|
|
31
|
+
n -> infinity, but a structurally distinct, celebrated result
|
|
32
|
+
with no Hermitian-ensemble analogue.
|
|
33
|
+
GinSE (quaternion): a real quaternion matrix, embedded as a 2n x 2n
|
|
34
|
+
complex matrix via the standard quaternion-to-2x2-complex-block
|
|
35
|
+
map, has exactly 2n eigenvalues occurring in complex-conjugate
|
|
36
|
+
pairs (z, z-bar) -- but, importantly, this is NOT the same kind
|
|
37
|
+
of degeneracy as GSE/CSE (where the paired eigenvalues are
|
|
38
|
+
*exactly identical*, a true Kramers symmetry): here z and
|
|
39
|
+
z-bar are genuinely distinct eigenvalues whenever z is not real,
|
|
40
|
+
so GinSE keeps all 2n eigenvalues rather than de-duplicating.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
import numpy as np
|
|
44
|
+
|
|
45
|
+
from .base import MatrixEnsemble
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class GinOE(MatrixEnsemble):
|
|
49
|
+
"""Real Ginibre ensemble.
|
|
50
|
+
|
|
51
|
+
``Spectrum.eigenvalues`` holds n complex values per sample (purely
|
|
52
|
+
real entries have zero imaginary part).
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
beta: float = 1
|
|
56
|
+
|
|
57
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
58
|
+
x = rng.standard_normal((self.n, self.n))
|
|
59
|
+
return np.linalg.eigvals(x).astype(complex)
|
|
60
|
+
|
|
61
|
+
def natural_scale(self) -> float:
|
|
62
|
+
return np.sqrt(self.n)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class GinUE(MatrixEnsemble):
|
|
66
|
+
"""Complex Ginibre ensemble."""
|
|
67
|
+
|
|
68
|
+
beta: float = 2
|
|
69
|
+
|
|
70
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
71
|
+
x = (rng.standard_normal((self.n, self.n)) + 1j * rng.standard_normal((self.n, self.n))) / np.sqrt(2.0)
|
|
72
|
+
return np.linalg.eigvals(x)
|
|
73
|
+
|
|
74
|
+
def natural_scale(self) -> float:
|
|
75
|
+
return np.sqrt(self.n)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class GinSE(MatrixEnsemble):
|
|
79
|
+
"""Quaternion Ginibre ensemble.
|
|
80
|
+
|
|
81
|
+
``n`` is the quaternion matrix dimension; each sample produces 2*n
|
|
82
|
+
complex eigenvalues (genuinely distinct conjugate pairs -- see
|
|
83
|
+
module docstring for why these are NOT de-duplicated, unlike GSE/CSE).
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
beta: float = 4
|
|
87
|
+
|
|
88
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
89
|
+
n = self.n
|
|
90
|
+
a = rng.standard_normal((n, n))
|
|
91
|
+
b = rng.standard_normal((n, n))
|
|
92
|
+
c = rng.standard_normal((n, n))
|
|
93
|
+
d = rng.standard_normal((n, n))
|
|
94
|
+
# Vectorized quaternion-to-2x2-complex-block embedding (verified
|
|
95
|
+
# against the direct per-block loop construction to give an
|
|
96
|
+
# identical matrix): strided assignment places each block
|
|
97
|
+
# [[a+bi, c+di], [-c+di, a-bi]] / 2 directly, without a Python
|
|
98
|
+
# loop over n^2 blocks. The /2 normalizes entry scale to match
|
|
99
|
+
# the same unit-disk circular law radius as GinOE/GinUE after
|
|
100
|
+
# the shared sqrt(n) natural_scale -- verified numerically (see
|
|
101
|
+
# design notes / test suite).
|
|
102
|
+
m = np.zeros((2 * n, 2 * n), dtype=complex)
|
|
103
|
+
m[0::2, 0::2] = (a + 1j * b) / 2.0
|
|
104
|
+
m[0::2, 1::2] = (c + 1j * d) / 2.0
|
|
105
|
+
m[1::2, 0::2] = (-c + 1j * d) / 2.0
|
|
106
|
+
m[1::2, 1::2] = (a - 1j * b) / 2.0
|
|
107
|
+
return np.linalg.eigvals(m)
|
|
108
|
+
|
|
109
|
+
def natural_scale(self) -> float:
|
|
110
|
+
return np.sqrt(self.n)
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""Girko ensembles -- universality generalizations of the Ginibre
|
|
2
|
+
ensembles.
|
|
3
|
+
|
|
4
|
+
References
|
|
5
|
+
----------
|
|
6
|
+
V. L. Girko, "Circular law", Theory Probab. Appl. 29 (1984) 694 -- the
|
|
7
|
+
circular law holds for ANY i.i.d. entry distribution (mean 0, unit
|
|
8
|
+
variance), not just Gaussian.
|
|
9
|
+
Z. D. Bai, "Circular law", Ann. Probab. 25 (1997) 494 -- fixed gaps in
|
|
10
|
+
Girko's original proof.
|
|
11
|
+
T. Tao, V. Vu (with an appendix by M. Krishnapur), "Random matrices:
|
|
12
|
+
Universality of ESDs and the circular law", Ann. Probab. 38 (2010) 2023
|
|
13
|
+
-- relaxed the moment condition to finite variance.
|
|
14
|
+
V. L. Girko, "The elliptic law", Theory Probab. Appl. 30 (1985) 677 --
|
|
15
|
+
matrices whose (X_ij, X_ji) entry pairs are correlated have eigenvalues
|
|
16
|
+
filling an ellipse rather than a disk.
|
|
17
|
+
A. Naumov, "Elliptic law for random matrices", arXiv:1201.1639 (2012) --
|
|
18
|
+
rigorous elliptic law for general (non-Gaussian) entry distributions.
|
|
19
|
+
Y. V. Fyodorov, B. A. Khoruzhenko, H.-J. Sommers, "Almost-Hermitian
|
|
20
|
+
random matrices: eigenvalue density in the complex plane", Phys. Lett.
|
|
21
|
+
A 226 (1997) 46 -- the Gaussian special case ("elliptic Ginibre"), which
|
|
22
|
+
:class:`GirkoElliptic` recovers for its default entry distribution --
|
|
23
|
+
per the design notes, FKS is a parametrization of the general Girko
|
|
24
|
+
result below, not a separate construction.
|
|
25
|
+
|
|
26
|
+
Both ensembles reuse ``physicskit.rmt.ensembles.universality``'s entry-sampler
|
|
27
|
+
convention (``uniform_unit_variance``, ``rademacher``,
|
|
28
|
+
``exponential_centered_unit_variance``) -- the same non-Gaussian entry
|
|
29
|
+
distributions already used there to demonstrate Wigner (Hermitian)
|
|
30
|
+
universality now demonstrate its non-Hermitian analogue: the circular
|
|
31
|
+
and elliptic laws depend only on the first two moments of the entry
|
|
32
|
+
distribution, not on Gaussianity.
|
|
33
|
+
|
|
34
|
+
Unlike every Dyson-indexed ensemble elsewhere in this package,
|
|
35
|
+
:class:`IIDEnsemble` and :class:`GirkoElliptic` have NO beta:
|
|
36
|
+
Girko universality is a different, orthogonal axis from Dyson's
|
|
37
|
+
threefold way -- it says the *macroscopic* eigenvalue law
|
|
38
|
+
(circular/elliptic) is unchanged across entry distributions, not that
|
|
39
|
+
the *exact* joint eigenvalue density matches a beta-ensemble formula
|
|
40
|
+
(only Gaussian entries have that property). ``Spectrum.beta`` is
|
|
41
|
+
therefore left ``None`` for both, inherited from the base class.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
from collections.abc import Callable
|
|
47
|
+
|
|
48
|
+
import numpy as np
|
|
49
|
+
|
|
50
|
+
from .base import MatrixEnsemble
|
|
51
|
+
|
|
52
|
+
EntrySampler = Callable[[np.random.Generator, tuple[int, ...]], np.ndarray]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def standard_normal(rng: np.random.Generator, size: tuple[int, ...]) -> np.ndarray:
|
|
56
|
+
"""i.i.d. standard Gaussian entries -- mean 0, variance 1. Default
|
|
57
|
+
entry distribution for :class:`GirkoElliptic` (the FKS elliptic
|
|
58
|
+
Ginibre special case); also usable directly with :class:`IIDEnsemble`
|
|
59
|
+
to recover plain Ginibre-law behavior via the general construction."""
|
|
60
|
+
return rng.standard_normal(size)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class IIDEnsemble(MatrixEnsemble):
|
|
64
|
+
"""Girko's circular law: eigenvalues of an n x n matrix with i.i.d.
|
|
65
|
+
mean-zero, unit-variance entries (real or complex) converge to the
|
|
66
|
+
uniform distribution on the unit disk after the sqrt(n) rescaling --
|
|
67
|
+
exactly the GinOE/GinUE limiting law, but for ANY entry distribution
|
|
68
|
+
satisfying those two moment conditions, not just Gaussian.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
n : int
|
|
73
|
+
entry_sampler : callable
|
|
74
|
+
``entry_sampler(rng, size)`` returning i.i.d. mean-zero,
|
|
75
|
+
unit-variance real samples of the given shape -- see
|
|
76
|
+
``standard_normal`` above and
|
|
77
|
+
``physicskit.rmt.ensembles.universality.uniform_unit_variance``,
|
|
78
|
+
``rademacher``, ``exponential_centered_unit_variance`` for
|
|
79
|
+
ready-made options.
|
|
80
|
+
complex_entries : bool, optional
|
|
81
|
+
If True (default), each entry is
|
|
82
|
+
``(entry_sampler + 1j*entry_sampler) / sqrt(2)`` -- matching
|
|
83
|
+
GinUE's construction. If False, entries are real -- matching
|
|
84
|
+
GinOE, including its real-eigenvalue anomaly at any finite n
|
|
85
|
+
(a property of real matrices generally, not just Gaussian ones).
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(
|
|
89
|
+
self,
|
|
90
|
+
n: int,
|
|
91
|
+
entry_sampler: EntrySampler,
|
|
92
|
+
complex_entries: bool = True,
|
|
93
|
+
seed: int | np.random.Generator | None = None,
|
|
94
|
+
) -> None:
|
|
95
|
+
super().__init__(n, seed=seed)
|
|
96
|
+
self.entry_sampler = entry_sampler
|
|
97
|
+
self.complex_entries = complex_entries
|
|
98
|
+
|
|
99
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
100
|
+
n = self.n
|
|
101
|
+
if self.complex_entries:
|
|
102
|
+
x = (self.entry_sampler(rng, (n, n)) + 1j * self.entry_sampler(rng, (n, n))) / np.sqrt(2.0)
|
|
103
|
+
else:
|
|
104
|
+
x = self.entry_sampler(rng, (n, n))
|
|
105
|
+
return np.linalg.eigvals(x).astype(complex)
|
|
106
|
+
|
|
107
|
+
def natural_scale(self) -> float:
|
|
108
|
+
return np.sqrt(self.n)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _correlated_pair_matrix(n: int, rho: float, entry_sampler: EntrySampler, rng: np.random.Generator) -> np.ndarray:
|
|
112
|
+
"""n x n real matrix whose off-diagonal entry pairs (X_ij, X_ji)
|
|
113
|
+
(i != j) each have unit variance and correlation ``rho``.
|
|
114
|
+
|
|
115
|
+
Construction: independent A, B ~ entry_sampler; for i < j,
|
|
116
|
+
X_ij = c1*A_ij + c2*B_ij
|
|
117
|
+
X_ji = c1*A_ij - c2*B_ij
|
|
118
|
+
with c1 = sqrt((1+rho)/2), c2 = sqrt((1-rho)/2). Directly verifiable:
|
|
119
|
+
Var(X_ij) = Var(X_ji) = c1^2 + c2^2 = 1 and
|
|
120
|
+
Cov(X_ij, X_ji) = c1^2 - c2^2 = rho, for any entry_sampler with
|
|
121
|
+
mean 0, unit variance -- not just Gaussian (Naumov 2012).
|
|
122
|
+
|
|
123
|
+
Diagonal entries are set to X_ii = c1*sqrt(2)*A_ii, giving
|
|
124
|
+
Var(X_ii) = 1+rho -- chosen (rather than a fixed unit variance) so
|
|
125
|
+
the construction degenerates *exactly* at both endpoints: at rho=1
|
|
126
|
+
(c1=1) this is sqrt(2)*A_ii, the standard Wigner/GOE
|
|
127
|
+
variance-2 diagonal convention used elsewhere in this package (see
|
|
128
|
+
``physicskit.rmt.ensembles.universality.GeneralWignerEnsemble``), and at
|
|
129
|
+
rho=-1 (c1=0) the diagonal is exactly zero -- required for X to be
|
|
130
|
+
exactly antisymmetric there (X_ii = -X_ii forces X_ii = 0; no
|
|
131
|
+
distribution of nonzero variance can satisfy that identically).
|
|
132
|
+
"""
|
|
133
|
+
a = entry_sampler(rng, (n, n))
|
|
134
|
+
b = entry_sampler(rng, (n, n))
|
|
135
|
+
c1 = np.sqrt((1.0 + rho) / 2.0)
|
|
136
|
+
c2 = np.sqrt((1.0 - rho) / 2.0)
|
|
137
|
+
upper = np.triu(c1 * a + c2 * b, k=1)
|
|
138
|
+
lower = np.triu(c1 * a - c2 * b, k=1).T
|
|
139
|
+
diagonal = np.diag(c1 * np.sqrt(2.0) * np.diag(a))
|
|
140
|
+
return upper + lower + diagonal
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
class GirkoElliptic(MatrixEnsemble):
|
|
144
|
+
"""Girko's elliptic law: eigenvalues of an n x n real matrix whose
|
|
145
|
+
(X_ij, X_ji) entry pairs are correlated at ``rho`` (each individually
|
|
146
|
+
unit variance) fill an ellipse with semi-axes (1+rho) along the real
|
|
147
|
+
axis and (1-rho) along the imaginary axis, after the sqrt(n)
|
|
148
|
+
rescaling.
|
|
149
|
+
|
|
150
|
+
rho=0 recovers plain i.i.d. entries -- Girko's circular law, a disk
|
|
151
|
+
of radius 1, the same limiting law as :class:`IIDEnsemble` with
|
|
152
|
+
``complex_entries=False``. The endpoints rho=+-1 are exact
|
|
153
|
+
(not just asymptotic) degenerate limits, checkable at any finite n:
|
|
154
|
+
rho=1 makes X exactly symmetric (X_ij = X_ji), so eigenvalues are
|
|
155
|
+
exactly real (recovering the real-entries Wigner/semicircle case,
|
|
156
|
+
the ellipse collapsed onto the real segment [-2, 2]); rho=-1 makes X
|
|
157
|
+
exactly antisymmetric, so eigenvalues are exactly purely imaginary
|
|
158
|
+
(real antisymmetric matrices have eigenvalues 0 or imaginary
|
|
159
|
+
conjugate pairs -- the ellipse collapsed onto the imaginary axis).
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
n : int
|
|
164
|
+
rho : float
|
|
165
|
+
Entry-pair correlation, in [-1, 1].
|
|
166
|
+
entry_sampler : callable, optional
|
|
167
|
+
``entry_sampler(rng, size)`` returning i.i.d. mean-zero,
|
|
168
|
+
unit-variance real samples (default: ``standard_normal``, i.e.
|
|
169
|
+
the Fyodorov-Khoruzhenko-Sommers elliptic Ginibre special case).
|
|
170
|
+
"""
|
|
171
|
+
|
|
172
|
+
def __init__(
|
|
173
|
+
self,
|
|
174
|
+
n: int,
|
|
175
|
+
rho: float,
|
|
176
|
+
entry_sampler: EntrySampler | None = None,
|
|
177
|
+
seed: int | np.random.Generator | None = None,
|
|
178
|
+
) -> None:
|
|
179
|
+
if not -1.0 <= rho <= 1.0:
|
|
180
|
+
raise ValueError(f"rho must be in [-1, 1], got {rho}")
|
|
181
|
+
super().__init__(n, seed=seed)
|
|
182
|
+
self.rho = float(rho)
|
|
183
|
+
self.entry_sampler = entry_sampler or standard_normal
|
|
184
|
+
|
|
185
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
186
|
+
x = _correlated_pair_matrix(self.n, self.rho, self.entry_sampler, rng)
|
|
187
|
+
return np.linalg.eigvals(x).astype(complex)
|
|
188
|
+
|
|
189
|
+
def natural_scale(self) -> float:
|
|
190
|
+
return np.sqrt(self.n)
|