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,92 @@
|
|
|
1
|
+
"""Random graph Laplacians -- spectral graph theory / network transport
|
|
2
|
+
ensembles, built on an Erdos-Renyi G(n, p) random graph.
|
|
3
|
+
|
|
4
|
+
References
|
|
5
|
+
----------
|
|
6
|
+
F. R. K. Chung, "Spectral Graph Theory", CBMS Regional Conference
|
|
7
|
+
Series in Mathematics 92, AMS, 1997 -- normalized Laplacian eigenvalues
|
|
8
|
+
lie in [0, 2] for any graph, with 2 attained iff a connected component
|
|
9
|
+
is bipartite (used below as ``natural_scale`` for the normalized case).
|
|
10
|
+
F. Chung, L. Lu, V. Vu, "Spectra of random graphs with given expected
|
|
11
|
+
degrees", Proc. Natl. Acad. Sci. 100 (2003) 6313 -- Erdos-Renyi Laplacian
|
|
12
|
+
spectral asymptotics.
|
|
13
|
+
|
|
14
|
+
Construction: an n x n symmetric 0/1 adjacency matrix A with i.i.d.
|
|
15
|
+
Bernoulli(p) entries above the diagonal (zero diagonal -- no self-loops),
|
|
16
|
+
mirrored below. Two Laplacian variants:
|
|
17
|
+
|
|
18
|
+
combinatorial: L = D - A (D = diag(degree))
|
|
19
|
+
normalized: L_sym = I - D^(-1/2) A D^(-1/2)
|
|
20
|
+
|
|
21
|
+
Isolated vertices (degree 0, possible whenever p is small enough) are
|
|
22
|
+
handled by convention: an isolated vertex contributes an exact zero row/
|
|
23
|
+
column to A, hence an exact zero eigenvalue to L trivially, and is
|
|
24
|
+
assigned D^(-1/2) = 0 for that vertex in L_sym (rather than raising a
|
|
25
|
+
division error) -- the standard spectral-graph-theory convention,
|
|
26
|
+
matching how an isolated vertex trivially decouples from graph
|
|
27
|
+
transport dynamics either way.
|
|
28
|
+
|
|
29
|
+
Unlike the semicircle/Marchenko-Pastur ensembles, the COMBINATORIAL
|
|
30
|
+
Laplacian's spectrum is not naturally centered at 0 (it concentrates
|
|
31
|
+
around the mean degree n*p for large n*p, since L = D - A and D
|
|
32
|
+
concentrates around n*p -- Chung-Lu-Vu), so ``natural_scale`` performs
|
|
33
|
+
no rescaling for that case (dividing by a single scalar cannot both
|
|
34
|
+
correctly center and rescale it); the NORMALIZED Laplacian, by contrast,
|
|
35
|
+
has an exact, graph-independent bound (eigenvalues in [0, 2]), so no
|
|
36
|
+
rescaling is needed there either, for the opposite reason.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import numpy as np
|
|
42
|
+
|
|
43
|
+
from .base import MatrixEnsemble
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _erdos_renyi_adjacency(n: int, p: float, rng: np.random.Generator) -> np.ndarray:
|
|
47
|
+
upper = np.triu(rng.random((n, n)) < p, k=1)
|
|
48
|
+
return upper.astype(float) + upper.T.astype(float)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class GraphLaplacianEnsemble(MatrixEnsemble):
|
|
52
|
+
"""Laplacian eigenvalues of an Erdos-Renyi G(n, p) random graph.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
n : int
|
|
57
|
+
Number of vertices.
|
|
58
|
+
p : float
|
|
59
|
+
Edge probability, in (0, 1].
|
|
60
|
+
normalized : bool, optional
|
|
61
|
+
If True (default False), use the normalized Laplacian
|
|
62
|
+
I - D^(-1/2) A D^(-1/2) (eigenvalues in [0, 2]) instead of the
|
|
63
|
+
combinatorial Laplacian D - A.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
n: int,
|
|
69
|
+
p: float,
|
|
70
|
+
normalized: bool = False,
|
|
71
|
+
seed: int | np.random.Generator | None = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
if not 0.0 < p <= 1.0:
|
|
74
|
+
raise ValueError(f"p must be in (0, 1], got {p}")
|
|
75
|
+
super().__init__(n, seed=seed)
|
|
76
|
+
self.p = float(p)
|
|
77
|
+
self.normalized = normalized
|
|
78
|
+
|
|
79
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
80
|
+
a = _erdos_renyi_adjacency(self.n, self.p, rng)
|
|
81
|
+
degree = a.sum(axis=1)
|
|
82
|
+
if self.normalized:
|
|
83
|
+
with np.errstate(divide="ignore"):
|
|
84
|
+
d_inv_sqrt = np.where(degree > 0, 1.0 / np.sqrt(degree), 0.0)
|
|
85
|
+
laplacian = np.eye(self.n) - (d_inv_sqrt[:, None] * a) * d_inv_sqrt[None, :]
|
|
86
|
+
else:
|
|
87
|
+
laplacian = np.diag(degree) - a
|
|
88
|
+
laplacian = (laplacian + laplacian.T) / 2.0
|
|
89
|
+
return np.linalg.eigvalsh(laplacian)
|
|
90
|
+
|
|
91
|
+
def natural_scale(self) -> float:
|
|
92
|
+
return 1.0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Eigenvalue statistics of a raw Haar-random orthogonal matrix --
|
|
2
|
+
deliberately distinct from the Circular Orthogonal Ensemble.
|
|
3
|
+
|
|
4
|
+
Reference: F. Mezzadri, "How to generate random matrices from the
|
|
5
|
+
classical compact groups", Notices Amer. Math. Soc. 54 (2007) 592.
|
|
6
|
+
|
|
7
|
+
As already flagged in ``physicskit.rmt.ensembles.circular``'s module docstring:
|
|
8
|
+
COE is NOT "eigenvalues of a Haar-random orthogonal matrix" -- a real
|
|
9
|
+
orthogonal matrix's eigenvalues are either real (+-1) or complex-
|
|
10
|
+
conjugate pairs on the unit circle, not the generic uniform-phase
|
|
11
|
+
structure a circular ensemble requires. This module provides that
|
|
12
|
+
other, genuinely different object directly, rather than leaving it as
|
|
13
|
+
only a cautionary note: the raw-matrix generators themselves
|
|
14
|
+
(``haar_orthogonal``, ``haar_unitary``, ``haar_symplectic``) live in
|
|
15
|
+
``physicskit.rmt.utils.haar``, since what they return is the group element
|
|
16
|
+
itself (useful directly as a random quantum gate / change of basis for
|
|
17
|
+
circuit-simulation and randomized-benchmarking use cases) rather than
|
|
18
|
+
an eigenvalue-only product -- the one architectural mismatch with every
|
|
19
|
+
other ensemble in this package, whose ``.sample()`` is built around
|
|
20
|
+
eigenvalues as the primary product (see ``physicskit.rmt.ensembles.base``).
|
|
21
|
+
``HaarUnitary``'s and ``HaarSymplectic``'s eigenvalue-phase statistics
|
|
22
|
+
are, respectively, already exactly what CUE and CSE model (see
|
|
23
|
+
``physicskit.rmt.ensembles.circular``), so no separate ensemble classes are
|
|
24
|
+
added here for those -- only the genuinely new, non-duplicate
|
|
25
|
+
Haar-orthogonal eigenvalue ensemble below.
|
|
26
|
+
|
|
27
|
+
Verified during development (see ``tests/test_haar.py``): unlike CUE
|
|
28
|
+
(zero real eigenvalues, generically) and COE (also generically no real
|
|
29
|
+
eigenvalues -- see ``physicskit.rmt.ensembles.circular``), a raw Haar-O(n)
|
|
30
|
+
matrix has a genuinely nonzero real-eigenvalue count whose expectation
|
|
31
|
+
stays O(1) (order unity, empirically close to 1) rather than vanishing
|
|
32
|
+
or growing, as n increases -- structurally distinct from both the
|
|
33
|
+
circular-ensemble and the GinOE real-eigenvalue-count stories (the
|
|
34
|
+
latter grows like sqrt(2n/pi), Edelman-Kostlan-Shub 1994).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
import numpy as np
|
|
38
|
+
|
|
39
|
+
from ..utils.haar import haar_orthogonal
|
|
40
|
+
from .base import MatrixEnsemble
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class HaarOrthogonalEnsemble(MatrixEnsemble):
|
|
44
|
+
"""Eigenvalues of a raw n x n Haar-random orthogonal matrix (O(n)).
|
|
45
|
+
|
|
46
|
+
``Spectrum.eigenvalues`` holds n complex values per sample (a mix of
|
|
47
|
+
real +-1 eigenvalues and complex-conjugate pairs on the unit
|
|
48
|
+
circle) -- see module docstring for why this is a distinct object
|
|
49
|
+
from :class:`~physicskit.rmt.ensembles.circular.COE`.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
53
|
+
o = haar_orthogonal(self.n, rng)
|
|
54
|
+
return np.linalg.eigvals(o)
|
|
55
|
+
|
|
56
|
+
def natural_scale(self) -> float:
|
|
57
|
+
return 1.0
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Jacobi (MANOVA) ensembles -- JOE (beta=1), JUE (beta=2).
|
|
2
|
+
|
|
3
|
+
References
|
|
4
|
+
----------
|
|
5
|
+
K. W. Wachter, "The limiting empirical measure of multiple discriminant
|
|
6
|
+
ratios", Ann. Statist. 8 (1980) 937 -- the limiting law (see
|
|
7
|
+
``physicskit.rmt.stats.wachter``).
|
|
8
|
+
L. Erdos, B. Farrell, "Local Eigenvalue Density for General MANOVA
|
|
9
|
+
Matrices" (arXiv:1207.0031) -- the construction and formula used here,
|
|
10
|
+
verified numerically during development (see design notes and
|
|
11
|
+
``tests/test_jacobi_wachter.py``).
|
|
12
|
+
|
|
13
|
+
Construction: given two independent Wishart-type matrices A, B (n x n,
|
|
14
|
+
built from m1 and m2 "samples" respectively, m1, m2 >= n), the Jacobi
|
|
15
|
+
ensemble matrix is
|
|
16
|
+
|
|
17
|
+
J = A (A + B)^{-1}
|
|
18
|
+
|
|
19
|
+
Unlike Gaussian/Wishart, this requires the actual dense matrices A and B
|
|
20
|
+
(not just their eigenvalues), since the ratio construction mixes them --
|
|
21
|
+
none of the tridiagonal/bidiagonal eigenvalue-only tricks used elsewhere
|
|
22
|
+
in this package apply here. J itself is generally NOT symmetric, but is
|
|
23
|
+
similar to the manifestly symmetric matrix
|
|
24
|
+
|
|
25
|
+
M = (A+B)^{-1/2} A (A+B)^{-1/2}
|
|
26
|
+
|
|
27
|
+
(via conjugation by (A+B)^{1/2}), which has the same eigenvalues and is
|
|
28
|
+
what's actually diagonalized here for numerical stability. Eigenvalues
|
|
29
|
+
are real and confined to [0, 1] (a "double hard edge", unlike the
|
|
30
|
+
semicircle/Marchenko-Pastur soft edges).
|
|
31
|
+
|
|
32
|
+
Parametrization: following Erdos-Farrell/Wachter, a = m1/n, b = m2/n
|
|
33
|
+
(inverse aspect ratios, i.e. "samples per variable" -- note this is the
|
|
34
|
+
reciprocal of the gamma = n/m convention used in
|
|
35
|
+
``physicskit.rmt.ensembles.wishart``).
|
|
36
|
+
|
|
37
|
+
beta=4 (quaternion, "JSE"): A and B are built from n x m quaternion
|
|
38
|
+
matrices, embedded as 2n x 2m complex matrices via the standard
|
|
39
|
+
quaternion-to-2x2-complex-block map (the same convention as
|
|
40
|
+
``physicskit.rmt.ensembles.ginibre.GinSE`` and
|
|
41
|
+
``physicskit.rmt.ensembles.effective_hamiltonian``'s beta=4 case). Since that
|
|
42
|
+
embedding is an algebra homomorphism, A, B, S = A+B, and
|
|
43
|
+
M = S^{-1/2} A S^{-1/2} are all quaternionic self-dual Hermitian, so M's
|
|
44
|
+
2n eigenvalues come in exact Kramers double-degenerate pairs -- verified
|
|
45
|
+
numerically during development to machine precision (~1e-15) before
|
|
46
|
+
being trusted here, the same true degeneracy GSE/CSE/EffGSE have (as
|
|
47
|
+
opposed to GinSE's merely-conjugate, genuinely distinct pairs). Only the
|
|
48
|
+
n distinct values are returned (one representative per pair), mirroring
|
|
49
|
+
those other beta=4 ensembles.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
from __future__ import annotations
|
|
53
|
+
|
|
54
|
+
import numpy as np
|
|
55
|
+
|
|
56
|
+
from .base import MatrixEnsemble
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _dense_wishart(n: int, m: int, rng: np.random.Generator, complex_entries: bool) -> np.ndarray:
|
|
60
|
+
if complex_entries:
|
|
61
|
+
x = (rng.standard_normal((n, m)) + 1j * rng.standard_normal((n, m))) / np.sqrt(2.0)
|
|
62
|
+
else:
|
|
63
|
+
x = rng.standard_normal((n, m))
|
|
64
|
+
return x @ x.conj().T
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _quaternion_gaussian_block(n: int, m: int, rng: np.random.Generator) -> np.ndarray:
|
|
68
|
+
"""N x M quaternion matrix, embedded as a 2N x 2M complex matrix
|
|
69
|
+
(same convention as ``physicskit.rmt.ensembles.ginibre.GinSE``)."""
|
|
70
|
+
a = rng.standard_normal((n, m))
|
|
71
|
+
b = rng.standard_normal((n, m))
|
|
72
|
+
c = rng.standard_normal((n, m))
|
|
73
|
+
d = rng.standard_normal((n, m))
|
|
74
|
+
w = np.zeros((2 * n, 2 * m), dtype=complex)
|
|
75
|
+
w[0::2, 0::2] = a + 1j * b
|
|
76
|
+
w[0::2, 1::2] = c + 1j * d
|
|
77
|
+
w[1::2, 0::2] = -c + 1j * d
|
|
78
|
+
w[1::2, 1::2] = a - 1j * b
|
|
79
|
+
return w / np.sqrt(2.0)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _dense_quaternion_wishart(n: int, m: int, rng: np.random.Generator) -> np.ndarray:
|
|
83
|
+
x = _quaternion_gaussian_block(n, m, rng)
|
|
84
|
+
return x @ x.conj().T
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class JacobiBetaEnsemble(MatrixEnsemble):
|
|
88
|
+
"""General (beta=1, 2, or 4) Jacobi/MANOVA ensemble.
|
|
89
|
+
|
|
90
|
+
``n`` is the ensemble/matrix size (per the ``MatrixEnsemble`` base
|
|
91
|
+
class, matching the other ensembles' convention -- for beta=4 this
|
|
92
|
+
is the number of *distinct* eigenvalues, per the module docstring);
|
|
93
|
+
``m1``, ``m2`` are the two Wishart "sample sizes" (each must be >= n).
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
n: int,
|
|
99
|
+
m1: int,
|
|
100
|
+
m2: int,
|
|
101
|
+
beta: int,
|
|
102
|
+
seed: int | np.random.Generator | None = None,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
n : int
|
|
108
|
+
Number of variables (matrix dimension).
|
|
109
|
+
m1 : int
|
|
110
|
+
Sample size of the first Wishart-type matrix; must be >= n.
|
|
111
|
+
m2 : int
|
|
112
|
+
Sample size of the second Wishart-type matrix; must be >= n.
|
|
113
|
+
beta : int
|
|
114
|
+
Dyson index; must be 1, 2, or 4.
|
|
115
|
+
seed : int, numpy.random.Generator, or None, optional
|
|
116
|
+
"""
|
|
117
|
+
if beta not in (1, 2, 4):
|
|
118
|
+
raise ValueError(f"JacobiBetaEnsemble only supports beta in (1, 2, 4), got {beta}")
|
|
119
|
+
if m1 < n or m2 < n:
|
|
120
|
+
raise ValueError(f"m1 (samples={m1}) and m2 (samples={m2}) must both be >= n (variables={n})")
|
|
121
|
+
super().__init__(n, seed=seed)
|
|
122
|
+
self.m1 = m1
|
|
123
|
+
self.m2 = m2
|
|
124
|
+
self.beta: float = beta
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def a(self) -> float:
|
|
128
|
+
"""Inverse aspect ratio m1/n (Erdos-Farrell/Wachter parametrization)."""
|
|
129
|
+
return self.m1 / self.n
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def b(self) -> float:
|
|
133
|
+
"""Inverse aspect ratio m2/n."""
|
|
134
|
+
return self.m2 / self.n
|
|
135
|
+
|
|
136
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
137
|
+
if self.beta == 4:
|
|
138
|
+
mat_a = _dense_quaternion_wishart(self.n, self.m1, rng)
|
|
139
|
+
mat_b = _dense_quaternion_wishart(self.n, self.m2, rng)
|
|
140
|
+
else:
|
|
141
|
+
complex_entries = self.beta == 2
|
|
142
|
+
mat_a = _dense_wishart(self.n, self.m1, rng, complex_entries)
|
|
143
|
+
mat_b = _dense_wishart(self.n, self.m2, rng, complex_entries)
|
|
144
|
+
s = mat_a + mat_b
|
|
145
|
+
w, v = np.linalg.eigh(s)
|
|
146
|
+
s_inv_sqrt = (v * (1.0 / np.sqrt(w))) @ v.conj().T
|
|
147
|
+
m = s_inv_sqrt @ mat_a @ s_inv_sqrt
|
|
148
|
+
m = (m + m.conj().T) / 2.0 # symmetrize away numerical asymmetry
|
|
149
|
+
eigenvalues = np.linalg.eigvalsh(m)
|
|
150
|
+
if self.beta == 4:
|
|
151
|
+
# Exact Kramers double degeneracy (see module docstring) --
|
|
152
|
+
# keep one representative per pair, as GSE/CSE/EffGSE do.
|
|
153
|
+
eigenvalues = np.sort(eigenvalues)[0::2]
|
|
154
|
+
return eigenvalues
|
|
155
|
+
|
|
156
|
+
def natural_scale(self) -> float:
|
|
157
|
+
# Eigenvalues already live in the natural [0, 1] range; no rescaling.
|
|
158
|
+
return 1.0
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class JOE(JacobiBetaEnsemble):
|
|
162
|
+
"""Jacobi Orthogonal Ensemble (beta=1): real double-Wishart ratio."""
|
|
163
|
+
|
|
164
|
+
def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
|
|
165
|
+
super().__init__(n, m1, m2, beta=1, seed=seed)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class JUE(JacobiBetaEnsemble):
|
|
169
|
+
"""Jacobi Unitary Ensemble (beta=2): complex double-Wishart ratio."""
|
|
170
|
+
|
|
171
|
+
def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
|
|
172
|
+
super().__init__(n, m1, m2, beta=2, seed=seed)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class JSE(JacobiBetaEnsemble):
|
|
176
|
+
"""Jacobi Symplectic Ensemble (beta=4): quaternionic double-Wishart
|
|
177
|
+
ratio. ``n`` is the quaternionic dimension; each sample produces n
|
|
178
|
+
distinct eigenvalues (each an exact Kramers pair internally -- see
|
|
179
|
+
module docstring)."""
|
|
180
|
+
|
|
181
|
+
def __init__(self, n: int, m1: int, m2: int, seed: int | np.random.Generator | None = None) -> None:
|
|
182
|
+
super().__init__(n, m1, m2, beta=4, seed=seed)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Poisson ensemble -- the integrable-system null model (Berry-Tabor
|
|
2
|
+
conjecture).
|
|
3
|
+
|
|
4
|
+
Reference
|
|
5
|
+
---------
|
|
6
|
+
M. V. Berry, M. Tabor, "Level clustering in the regular spectrum",
|
|
7
|
+
Proc. R. Soc. Lond. A 356 (1977) 375 -- the semiclassical argument that
|
|
8
|
+
quantized energy levels of a (generic) classically INTEGRABLE system are,
|
|
9
|
+
locally, statistically indistinguishable from an uncorrelated Poisson
|
|
10
|
+
point process, in sharp contrast to the level REPULSION seen in
|
|
11
|
+
GOE/GUE/GSE for classically CHAOTIC systems (the complementary
|
|
12
|
+
Bohigas-Giannoni-Schmit conjecture, Phys. Rev. Lett. 52 (1984) 1).
|
|
13
|
+
|
|
14
|
+
This is not a matrix ensemble in the sense every other class in this
|
|
15
|
+
package is -- there is no underlying random matrix, Dyson index, or
|
|
16
|
+
symmetry class here. It is the standard baseline/null model that RMT
|
|
17
|
+
level statistics (spacing distribution, ratio statistic, number
|
|
18
|
+
variance) are always contrasted against; see
|
|
19
|
+
``physicskit.rmt.stats.rigidity.number_variance_poisson``, which until this
|
|
20
|
+
ensemble existed had no matching sampler in this package to validate it
|
|
21
|
+
against empirically (see ``tests/test_poisson.py``).
|
|
22
|
+
|
|
23
|
+
Construction: n levels are generated as the partial sums of n i.i.d.
|
|
24
|
+
Exponential(1) spacings -- i.e. a single realization of a homogeneous
|
|
25
|
+
Poisson point process of UNIT RATE on the positive real line. This is
|
|
26
|
+
deliberately NOT "n i.i.d. Uniform(0, n) points" (a common shortcut):
|
|
27
|
+
that construction only approaches Poisson-process statistics
|
|
28
|
+
asymptotically in the bulk (finite-interval edge effects bias it at
|
|
29
|
+
finite n), whereas partial sums of i.i.d. Exponential(1) spacings give
|
|
30
|
+
EXACTLY unit-rate Poisson-process statistics at every n -- consecutive
|
|
31
|
+
spacings are exactly i.i.d. Exponential(1) by construction, not merely
|
|
32
|
+
on average.
|
|
33
|
+
|
|
34
|
+
Because the process has constant unit rate everywhere (translation
|
|
35
|
+
invariant), it needs no unfolding: unlike GOE/Wishart's compact,
|
|
36
|
+
nontrivially-shaped limiting density (which spacing/rigidity statistics
|
|
37
|
+
must first divide out via a CDF -- see ``physicskit.rmt.stats.unfolding``), the
|
|
38
|
+
levels sampled here already have exactly unit mean spacing throughout --
|
|
39
|
+
the same "no separate unfolding step needed" situation as the circular
|
|
40
|
+
ensembles (``physicskit.rmt.ensembles.circular``), though for the opposite
|
|
41
|
+
reason (uniform density on an unbounded line here, vs. exact rotational
|
|
42
|
+
invariance on a compact circle there).
|
|
43
|
+
|
|
44
|
+
``beta`` is left ``None`` (inherited from the base class): Poisson
|
|
45
|
+
statistics are not part of Dyson's threefold way at all -- they are the
|
|
46
|
+
uncorrelated baseline every beta-ensemble's level repulsion is
|
|
47
|
+
contrasted against, NOT a beta -> 0 limit of the Gaussian-tailed Wigner-
|
|
48
|
+
surmise family used for GOE/GUE/GSE. That limit is a half-Gaussian
|
|
49
|
+
(Rayleigh-shaped) distribution, not the exponential spacing law of a
|
|
50
|
+
true Poisson process -- worth knowing if you're tempted to reuse
|
|
51
|
+
``physicskit.rmt.validation.WignerSurmise(beta=0)`` or
|
|
52
|
+
``physicskit.rmt.validation.RatioDistribution(beta=0)`` here; both would
|
|
53
|
+
silently validate against the wrong theoretical curve. The exact
|
|
54
|
+
Poisson-process laws instead are: spacing PDF exp(-s) (``scipy.stats.expon``);
|
|
55
|
+
and, for the min/max-normalized ratio statistic r in [0, 1] used by
|
|
56
|
+
``physicskit.rmt.stats.ratios.ratio_statistics``, PDF 2/(1+r)**2, CDF 2r/(1+r)
|
|
57
|
+
-- derived directly here (not taken from a remembered formula) from the
|
|
58
|
+
ratio of two i.i.d. Exponential(1) variables, and checked numerically in
|
|
59
|
+
the test suite.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
import numpy as np
|
|
63
|
+
|
|
64
|
+
from .base import MatrixEnsemble
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PoissonEnsemble(MatrixEnsemble):
|
|
68
|
+
"""n "energy levels" of an integrable system's Berry-Tabor null
|
|
69
|
+
model: a unit-rate homogeneous Poisson point process, realized as
|
|
70
|
+
the partial sums of n i.i.d. Exponential(1) spacings.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
74
|
+
return np.cumsum(rng.exponential(scale=1.0, size=self.n))
|
|
75
|
+
|
|
76
|
+
def natural_scale(self) -> float:
|
|
77
|
+
return 1.0
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Polynomial ensembles -- squared singular values of a product of
|
|
2
|
+
independent Ginibre matrices; a genuine BIORTHOGONAL ensemble (Borodin's
|
|
3
|
+
general determinantal framework) whenever more than one factor is used.
|
|
4
|
+
|
|
5
|
+
References
|
|
6
|
+
----------
|
|
7
|
+
A. B. J. Kuijlaars, D. Stivigny, "Singular values of products of random
|
|
8
|
+
matrices and polynomial ensembles", Random Matrices Theory Appl. 3
|
|
9
|
+
(2014) 1450011 -- the "polynomial ensemble" terminology, and the theorem
|
|
10
|
+
that products of Ginibre matrices realize one, with explicit
|
|
11
|
+
Meijer-G-function biorthogonal weight functions.
|
|
12
|
+
A. Borodin, "Biorthogonal ensembles", Nucl. Phys. B 536 (1998) 704 --
|
|
13
|
+
the general determinantal framework: joint density
|
|
14
|
+
prod_{i<j}(x_i-x_j) * det[phi_k(x_j)] * det[psi_k(x_j)], of which the
|
|
15
|
+
classical orthogonal-polynomial ensembles (a single factor, L=1 below)
|
|
16
|
+
are the special case phi_k = psi_k (true orthogonal polynomials).
|
|
17
|
+
E. Strahov, "Differential equations for singular values of products of
|
|
18
|
+
Ginibre random matrices", J. Phys. A 47 (2014) 325203 -- the explicit
|
|
19
|
+
non-orthogonal-polynomial biorthogonal kernel for L >= 2, confirming
|
|
20
|
+
that construction is NOT reducible to a classical orthogonal-polynomial
|
|
21
|
+
ensemble.
|
|
22
|
+
G. Akemann, J. R. Ipsen, M. Kieburg, "Products of rectangular random
|
|
23
|
+
matrices: singular values and progressive scattering", Phys. Rev. E 88
|
|
24
|
+
(2013) 052118.
|
|
25
|
+
K. A. Penson, K. Zyczkowski, "Product of Ginibre matrices: Fuss-Catalan
|
|
26
|
+
and Raney distributions", Phys. Rev. E 83 (2011) 061118 -- the
|
|
27
|
+
Fuss-Catalan limiting law and moment formula used for validation here.
|
|
28
|
+
|
|
29
|
+
Construction
|
|
30
|
+
------------
|
|
31
|
+
X = X_1 @ X_2 @ ... @ X_L, each X_k an independent n x n Ginibre matrix
|
|
32
|
+
(real, beta=1, or complex, beta=2). This ensemble's eigenvalues are the
|
|
33
|
+
squared singular values of X, i.e. the eigenvalues of X^dagger @ X.
|
|
34
|
+
|
|
35
|
+
L=1 recovers the classical Wishart/Laguerre ensemble EXACTLY (a genuine
|
|
36
|
+
orthogonal-polynomial ensemble, phi_k=psi_k in Borodin's framework) --
|
|
37
|
+
used as a strong validation anchor in the test suite (a direct match
|
|
38
|
+
against the already-validated ``LaguerreBetaEnsemble``/
|
|
39
|
+
``MarchenkoPastur`` machinery), not a separate approximation.
|
|
40
|
+
|
|
41
|
+
L >= 2 is a GENUINE biorthogonal ensemble: Kuijlaars-Stivigny prove the
|
|
42
|
+
joint eigenvalue density still has the determinantal
|
|
43
|
+
Delta(x) * det[phi_k(x_j)] * det[psi_k(x_j)] form (making it a
|
|
44
|
+
"polynomial ensemble"), but Strahov shows the correlation kernel is
|
|
45
|
+
provably NOT the reproducing kernel of any single family of orthogonal
|
|
46
|
+
polynomials once L >= 2 -- phi_k and psi_k are then genuinely different
|
|
47
|
+
function families (built from Meijer G-functions), the defining feature
|
|
48
|
+
that distinguishes a biorthogonal ensemble from the classical
|
|
49
|
+
orthogonal-polynomial ensembles making up most of the rest of this
|
|
50
|
+
package. ``num_factors`` therefore defaults to 2 here, not 1, so the
|
|
51
|
+
default instance actually exhibits this genuine biorthogonal structure
|
|
52
|
+
rather than silently reducing to the classical case.
|
|
53
|
+
|
|
54
|
+
After the standard n**L rescaling, the limiting eigenvalue distribution
|
|
55
|
+
is the Fuss-Catalan law with parameter L (Penson-Zyczkowski 2011):
|
|
56
|
+
support [0, (L+1)**(L+1) / L**L] and exact moments
|
|
57
|
+
|
|
58
|
+
m_k = binom((L+1)*k, k) / (L*k + 1)
|
|
59
|
+
|
|
60
|
+
(L=1 recovers the Marchenko-Pastur moments at gamma=1, i.e. the Catalan
|
|
61
|
+
numbers, as it must). ``natural_scale`` divides by this n**L factor
|
|
62
|
+
accordingly -- both the scaling exponent and the exact moment formula
|
|
63
|
+
were verified numerically during development, at L=1..4, against Monte
|
|
64
|
+
Carlo before being used here (not assumed from memory) -- see
|
|
65
|
+
``tests/test_polynomial.py``.
|
|
66
|
+
|
|
67
|
+
Only beta in {1, 2} are supported here (real or complex Ginibre
|
|
68
|
+
factors), matching the precedent set by
|
|
69
|
+
``physicskit.rmt.ensembles.universality.GeneralWignerEnsemble``: a quaternionic
|
|
70
|
+
(beta=4) product-of-Ginibre construction is a further, separate
|
|
71
|
+
extension not implemented here.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
from __future__ import annotations
|
|
75
|
+
|
|
76
|
+
from math import comb
|
|
77
|
+
|
|
78
|
+
import numpy as np
|
|
79
|
+
|
|
80
|
+
from .base import MatrixEnsemble
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def fuss_catalan_moment(k: int, num_factors: int) -> float:
|
|
84
|
+
"""Exact k-th moment of the Fuss-Catalan(L) distribution -- the
|
|
85
|
+
limiting law of a product of L independent square Ginibre matrices'
|
|
86
|
+
squared singular values, after the n**L rescaling.
|
|
87
|
+
|
|
88
|
+
m_k = binom((L+1)*k, k) / (L*k + 1). L=1 recovers the standard
|
|
89
|
+
Marchenko-Pastur moments at gamma=1 (Catalan numbers).
|
|
90
|
+
"""
|
|
91
|
+
return comb((num_factors + 1) * k, k) / (num_factors * k + 1)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class PolynomialEnsemble(MatrixEnsemble):
|
|
95
|
+
"""Squared singular values of a product of ``num_factors`` independent
|
|
96
|
+
n x n Ginibre matrices -- a polynomial/biorthogonal ensemble (see
|
|
97
|
+
module docstring).
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
n : int
|
|
102
|
+
num_factors : int, optional
|
|
103
|
+
Number of independent Ginibre factors L (default 2, the
|
|
104
|
+
smallest case exhibiting genuine biorthogonal, non-orthogonal-
|
|
105
|
+
polynomial structure; L=1 recovers the classical Wishart/
|
|
106
|
+
Laguerre ensemble exactly).
|
|
107
|
+
beta : int, optional
|
|
108
|
+
1 (real Ginibre factors) or 2 (complex Ginibre factors).
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
def __init__(
|
|
112
|
+
self,
|
|
113
|
+
n: int,
|
|
114
|
+
num_factors: int = 2,
|
|
115
|
+
beta: int = 2,
|
|
116
|
+
seed: int | np.random.Generator | None = None,
|
|
117
|
+
) -> None:
|
|
118
|
+
if num_factors < 1:
|
|
119
|
+
raise ValueError(f"num_factors must be >= 1, got {num_factors}")
|
|
120
|
+
if beta not in (1, 2):
|
|
121
|
+
raise ValueError(f"beta must be 1 or 2, got {beta}")
|
|
122
|
+
super().__init__(n, seed=seed)
|
|
123
|
+
self.num_factors = num_factors
|
|
124
|
+
self.beta: float = beta
|
|
125
|
+
|
|
126
|
+
def _sample_factor(self, rng: np.random.Generator) -> np.ndarray:
|
|
127
|
+
n = self.n
|
|
128
|
+
if self.beta == 1:
|
|
129
|
+
return rng.standard_normal((n, n))
|
|
130
|
+
return (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
|
|
131
|
+
|
|
132
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
133
|
+
x = self._sample_factor(rng)
|
|
134
|
+
for _ in range(self.num_factors - 1):
|
|
135
|
+
x = x @ self._sample_factor(rng)
|
|
136
|
+
return np.linalg.eigvalsh(x.conj().T @ x)
|
|
137
|
+
|
|
138
|
+
def natural_scale(self) -> float:
|
|
139
|
+
return float(self.n) ** self.num_factors
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""PT-symmetric (pseudo-Hermitian) random matrix ensembles.
|
|
2
|
+
|
|
3
|
+
References
|
|
4
|
+
----------
|
|
5
|
+
C. M. Bender, S. Boettcher, "Real Spectra in Non-Hermitian Hamiltonians
|
|
6
|
+
Having PT Symmetry", Phys. Rev. Lett. 80 (1998) 5243 -- the original
|
|
7
|
+
PT-symmetric-quantum-mechanics observation this construction realizes
|
|
8
|
+
as a random matrix model.
|
|
9
|
+
A. Mostafazadeh, "Pseudo-Hermiticity versus PT-symmetry", J. Math. Phys.
|
|
10
|
+
43 (2002) 205, 2814, 3944 -- the equivalence (for a diagonalizable H
|
|
11
|
+
with real spectrum) between PT-symmetry and pseudo-Hermiticity
|
|
12
|
+
P H P^{-1} = H^dagger for some Hermitian, invertible P, which is the
|
|
13
|
+
concrete condition realized here (P chosen as a fixed, real, diagonal
|
|
14
|
+
+-1 "signature" matrix, so P = P^dagger = P^{-1}).
|
|
15
|
+
|
|
16
|
+
Construction: partition the n = p + q dimensions into a "+1" block
|
|
17
|
+
(size p) and a "-1" block (size q), P = diag(I_p, -I_q). Writing H in
|
|
18
|
+
the matching 2x2 block form,
|
|
19
|
+
|
|
20
|
+
H = [[A, B], [C, D]]
|
|
21
|
+
|
|
22
|
+
the pseudo-Hermiticity condition P H P = H^dagger forces A = A^dagger
|
|
23
|
+
(p x p Hermitian), D = D^dagger (q x q Hermitian), and C = -B^dagger
|
|
24
|
+
(NOT the +B^dagger a genuinely Hermitian H would need) -- verified
|
|
25
|
+
numerically here (``max|PHP - H^dagger|`` to machine precision) before
|
|
26
|
+
being trusted. A, D are drawn as independent GOE/GUE-type Hermitian
|
|
27
|
+
blocks; B is an independent p x q Ginibre-type block scaled by a
|
|
28
|
+
non-Hermiticity/coupling parameter ``g``.
|
|
29
|
+
|
|
30
|
+
Physics (verified numerically during development, see
|
|
31
|
+
``tests/test_pt_symmetric.py``): at g=0, H is block-diagonal (A
|
|
32
|
+
directly summed with D), so its spectrum is trivially real (each block
|
|
33
|
+
is Hermitian) -- the fully "unbroken" PT-symmetric phase. Since H is
|
|
34
|
+
similar to H^dagger (via the invertible P), its eigenvalue set is
|
|
35
|
+
always closed under complex conjugation: every eigenvalue is either
|
|
36
|
+
real or part of an exact complex-conjugate pair -- never an isolated
|
|
37
|
+
complex value. As g increases from 0, an increasing fraction of
|
|
38
|
+
eigenvalues collide in real-conjugate pairs and move off the real axis
|
|
39
|
+
as complex-conjugate pairs (each such collision is an "exceptional
|
|
40
|
+
point": both the eigenvalues AND their eigenvectors coalesce there,
|
|
41
|
+
unlike an ordinary level crossing) -- the celebrated PT-symmetry-
|
|
42
|
+
breaking transition. See ``physicskit.rmt.stats.pt_symmetric`` for the fraction-
|
|
43
|
+
of-real-eigenvalues statistic that tracks this transition.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
from __future__ import annotations
|
|
47
|
+
|
|
48
|
+
import numpy as np
|
|
49
|
+
|
|
50
|
+
from .base import MatrixEnsemble
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _dense_hermitian_block(n: int, rng: np.random.Generator, complex_entries: bool) -> np.ndarray:
|
|
54
|
+
if complex_entries:
|
|
55
|
+
x = (rng.standard_normal((n, n)) + 1j * rng.standard_normal((n, n))) / np.sqrt(2.0)
|
|
56
|
+
return (x + x.conj().T) / 2.0
|
|
57
|
+
x = rng.standard_normal((n, n))
|
|
58
|
+
return (x + x.T) / np.sqrt(2.0)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class PTSymmetricEnsemble(MatrixEnsemble):
|
|
62
|
+
"""Pseudo-Hermitian (PT-symmetric-realizing) random matrix ensemble
|
|
63
|
+
(see module docstring). ``n`` (inherited from ``MatrixEnsemble``) is
|
|
64
|
+
the total dimension p + q.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
p : int
|
|
69
|
+
Size of the "+1"-signature block.
|
|
70
|
+
q : int
|
|
71
|
+
Size of the "-1"-signature block.
|
|
72
|
+
g : float
|
|
73
|
+
Non-Hermiticity / coupling strength scaling the off-diagonal
|
|
74
|
+
block B (g=0: block-diagonal, trivially real spectrum; larger
|
|
75
|
+
g: more eigenvalues driven into complex-conjugate pairs).
|
|
76
|
+
beta : int, optional
|
|
77
|
+
1 (real blocks) or 2 (complex Hermitian blocks, default).
|
|
78
|
+
seed : int, numpy.random.Generator, or None, optional
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def __init__(
|
|
82
|
+
self,
|
|
83
|
+
p: int,
|
|
84
|
+
q: int,
|
|
85
|
+
g: float,
|
|
86
|
+
beta: int = 2,
|
|
87
|
+
seed: int | np.random.Generator | None = None,
|
|
88
|
+
) -> None:
|
|
89
|
+
if beta not in (1, 2):
|
|
90
|
+
raise ValueError(f"PTSymmetricEnsemble only supports beta in (1, 2), got {beta}")
|
|
91
|
+
if g < 0:
|
|
92
|
+
raise ValueError(f"g (coupling strength) must be >= 0, got {g}")
|
|
93
|
+
super().__init__(p + q, seed=seed)
|
|
94
|
+
self.p = p
|
|
95
|
+
self.q = q
|
|
96
|
+
self.g = float(g)
|
|
97
|
+
self.beta: float = beta
|
|
98
|
+
|
|
99
|
+
def _sample_matrix(self, rng: np.random.Generator) -> np.ndarray:
|
|
100
|
+
complex_entries = self.beta == 2
|
|
101
|
+
a = _dense_hermitian_block(self.p, rng, complex_entries)
|
|
102
|
+
d = _dense_hermitian_block(self.q, rng, complex_entries)
|
|
103
|
+
if complex_entries:
|
|
104
|
+
b = self.g * (rng.standard_normal((self.p, self.q)) + 1j * rng.standard_normal((self.p, self.q))) / np.sqrt(2.0)
|
|
105
|
+
else:
|
|
106
|
+
b = self.g * rng.standard_normal((self.p, self.q))
|
|
107
|
+
c = -b.conj().T
|
|
108
|
+
top = np.hstack([a, b])
|
|
109
|
+
bottom = np.hstack([c, d])
|
|
110
|
+
return np.vstack([top, bottom])
|
|
111
|
+
|
|
112
|
+
def _sample_eigenvalues(self, rng: np.random.Generator) -> np.ndarray:
|
|
113
|
+
return np.linalg.eigvals(self._sample_matrix(rng)).astype(complex)
|
|
114
|
+
|
|
115
|
+
def natural_scale(self) -> float:
|
|
116
|
+
# No single universal rescaling: unlike the Hermitian ensembles,
|
|
117
|
+
# this ensemble's spectral shape genuinely changes character
|
|
118
|
+
# (real -> partially complex) as g varies, so there is no fixed
|
|
119
|
+
# limiting law to normalize against.
|
|
120
|
+
return 1.0
|