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,305 @@
|
|
|
1
|
+
"""Site and bond percolation on the square lattice, via Hoshen-Kopelman cluster labeling.
|
|
2
|
+
|
|
3
|
+
Percolation theory studies the sudden, geometric phase transition that
|
|
4
|
+
appears when a fraction ``p`` of a lattice's sites (or bonds) are randomly
|
|
5
|
+
occupied: below a critical threshold :math:`p_c` only small, finite clusters
|
|
6
|
+
exist, while above it a single cluster spans the entire system. The
|
|
7
|
+
transition is purely geometric -- there is no energy scale or temperature --
|
|
8
|
+
yet it shares the full apparatus of critical phenomena: a sharp threshold,
|
|
9
|
+
power-law cluster-size distributions, and universal critical exponents.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
__all__ = ["Percolation2D", "hoshen_kopelman"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def hoshen_kopelman(occupied):
|
|
20
|
+
"""Label connected clusters of occupied sites with the Hoshen-Kopelman algorithm.
|
|
21
|
+
|
|
22
|
+
A single raster pass assigns provisional labels while a union-find
|
|
23
|
+
structure tracks label equivalences (created whenever a site connects two
|
|
24
|
+
previously separate provisional clusters); a second pass resolves every
|
|
25
|
+
label to its cluster's root.
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
occupied : ndarray of shape (L, L), dtype bool
|
|
30
|
+
Site occupation grid (open boundary conditions -- no wraparound).
|
|
31
|
+
|
|
32
|
+
Returns
|
|
33
|
+
-------
|
|
34
|
+
ndarray of shape (L, L), dtype int64
|
|
35
|
+
Cluster label at each site; ``0`` marks unoccupied sites, and
|
|
36
|
+
occupied sites sharing a label belong to the same connected cluster.
|
|
37
|
+
Labels are not necessarily contiguous integers.
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
--------
|
|
41
|
+
>>> grid = np.array([[True, True, False], [False, True, False], [False, False, True]])
|
|
42
|
+
>>> labels = hoshen_kopelman(grid)
|
|
43
|
+
>>> bool(labels[0, 0] == labels[0, 1] == labels[1, 1])
|
|
44
|
+
True
|
|
45
|
+
>>> bool(labels[2, 2] not in (0, labels[0, 0]))
|
|
46
|
+
True
|
|
47
|
+
"""
|
|
48
|
+
L0, L1 = occupied.shape
|
|
49
|
+
labels = np.zeros((L0, L1), dtype=np.int64)
|
|
50
|
+
parent = [0]
|
|
51
|
+
|
|
52
|
+
def find(x):
|
|
53
|
+
root = x
|
|
54
|
+
while parent[root] != root:
|
|
55
|
+
root = parent[root]
|
|
56
|
+
while parent[x] != root:
|
|
57
|
+
parent[x], x = root, parent[x]
|
|
58
|
+
return root
|
|
59
|
+
|
|
60
|
+
def union(x, y):
|
|
61
|
+
rx, ry = find(x), find(y)
|
|
62
|
+
if rx != ry:
|
|
63
|
+
parent[max(rx, ry)] = min(rx, ry)
|
|
64
|
+
|
|
65
|
+
next_label = 1
|
|
66
|
+
for i in range(L0):
|
|
67
|
+
for j in range(L1):
|
|
68
|
+
if not occupied[i, j]:
|
|
69
|
+
continue
|
|
70
|
+
left = labels[i, j - 1] if j > 0 and occupied[i, j - 1] else 0
|
|
71
|
+
up = labels[i - 1, j] if i > 0 and occupied[i - 1, j] else 0
|
|
72
|
+
if left == 0 and up == 0:
|
|
73
|
+
labels[i, j] = next_label
|
|
74
|
+
parent.append(next_label)
|
|
75
|
+
next_label += 1
|
|
76
|
+
elif left != 0 and up == 0:
|
|
77
|
+
labels[i, j] = find(left)
|
|
78
|
+
elif left == 0 and up != 0:
|
|
79
|
+
labels[i, j] = find(up)
|
|
80
|
+
else:
|
|
81
|
+
union(left, up)
|
|
82
|
+
labels[i, j] = find(left)
|
|
83
|
+
|
|
84
|
+
for i in range(L0):
|
|
85
|
+
for j in range(L1):
|
|
86
|
+
if labels[i, j] != 0:
|
|
87
|
+
labels[i, j] = find(labels[i, j])
|
|
88
|
+
return labels
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class Percolation2D:
|
|
92
|
+
"""Site or bond percolation on a periodic-free (open-boundary) square lattice.
|
|
93
|
+
|
|
94
|
+
Parameters
|
|
95
|
+
----------
|
|
96
|
+
L : int, default=64
|
|
97
|
+
Linear lattice size.
|
|
98
|
+
p : float, default=0.5
|
|
99
|
+
Occupation probability: fraction of sites (site percolation) or
|
|
100
|
+
bonds (bond percolation) that are open.
|
|
101
|
+
mode : {"site", "bond"}, default="site"
|
|
102
|
+
Percolation type. Known exact thresholds for the square lattice are
|
|
103
|
+
:math:`p_c \\approx 0.592746` (site) and :math:`p_c = 0.5` (bond,
|
|
104
|
+
exact by self-duality).
|
|
105
|
+
seed : int, optional
|
|
106
|
+
Seed for reproducible lattice realizations.
|
|
107
|
+
|
|
108
|
+
Attributes
|
|
109
|
+
----------
|
|
110
|
+
labels : ndarray of shape (L, L)
|
|
111
|
+
Cluster labels of the current realization, from
|
|
112
|
+
:func:`hoshen_kopelman`.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
#: Known threshold for square-lattice site percolation (numerical estimate).
|
|
116
|
+
P_C_SITE = 0.592746
|
|
117
|
+
#: Exact threshold for square-lattice bond percolation (self-duality).
|
|
118
|
+
P_C_BOND = 0.5
|
|
119
|
+
|
|
120
|
+
def __init__(self, L=64, p=0.5, mode="site", seed=None):
|
|
121
|
+
if mode not in ("site", "bond"):
|
|
122
|
+
raise ValueError("mode must be 'site' or 'bond'")
|
|
123
|
+
self.L = L
|
|
124
|
+
self.p = p
|
|
125
|
+
self.mode = mode
|
|
126
|
+
self._rng = np.random.default_rng(seed)
|
|
127
|
+
self.labels = None
|
|
128
|
+
self.generate()
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def p_c(self):
|
|
132
|
+
"""Threshold occupation probability for the current ``mode``."""
|
|
133
|
+
return self.P_C_SITE if self.mode == "site" else self.P_C_BOND
|
|
134
|
+
|
|
135
|
+
def generate(self, p=None):
|
|
136
|
+
"""Draw a fresh random realization and label its clusters.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
p : float, optional
|
|
141
|
+
Occupation probability to use; defaults to ``self.p``. If given,
|
|
142
|
+
also updates ``self.p``.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
ndarray of shape (L, L)
|
|
147
|
+
The updated cluster ``labels`` array.
|
|
148
|
+
"""
|
|
149
|
+
if p is not None:
|
|
150
|
+
self.p = p
|
|
151
|
+
if self.mode == "site":
|
|
152
|
+
occupied = self._rng.random((self.L, self.L)) < self.p
|
|
153
|
+
else:
|
|
154
|
+
occupied = self._bond_occupation_to_sites(self.p)
|
|
155
|
+
self.labels = hoshen_kopelman(occupied)
|
|
156
|
+
return self.labels
|
|
157
|
+
|
|
158
|
+
def _bond_occupation_to_sites(self, p):
|
|
159
|
+
"""Build an effective all-occupied site grid whose HK labeling respects bond connectivity.
|
|
160
|
+
|
|
161
|
+
Bond percolation is reduced to Hoshen-Kopelman site labeling by
|
|
162
|
+
working on a doubled lattice: real sites at even indices, and
|
|
163
|
+
"bond sites" at the odd indices between them, present only when the
|
|
164
|
+
corresponding bond is open.
|
|
165
|
+
"""
|
|
166
|
+
L = self.L
|
|
167
|
+
expanded = np.zeros((2 * L - 1, 2 * L - 1), dtype=bool)
|
|
168
|
+
expanded[0::2, 0::2] = True # all real sites present
|
|
169
|
+
|
|
170
|
+
h_bonds = self._rng.random((L, L - 1)) < p # horizontal bonds
|
|
171
|
+
v_bonds = self._rng.random((L - 1, L)) < p # vertical bonds
|
|
172
|
+
expanded[0::2, 1::2] = h_bonds
|
|
173
|
+
expanded[1::2, 0::2] = v_bonds
|
|
174
|
+
return expanded
|
|
175
|
+
|
|
176
|
+
def spanning_labels(self):
|
|
177
|
+
"""Cluster labels of the current realization that span top to bottom.
|
|
178
|
+
|
|
179
|
+
For bond percolation the doubled lattice is used internally, but
|
|
180
|
+
returned labels index the real (even-index) sites.
|
|
181
|
+
|
|
182
|
+
Returns
|
|
183
|
+
-------
|
|
184
|
+
ndarray
|
|
185
|
+
Labels present on both the first and last row of real sites.
|
|
186
|
+
"""
|
|
187
|
+
if self.mode == "site":
|
|
188
|
+
top_labels = set(self.labels[0, :]) - {0}
|
|
189
|
+
bottom_labels = set(self.labels[-1, :]) - {0}
|
|
190
|
+
else:
|
|
191
|
+
top_labels = set(self.labels[0, 0::2]) - {0}
|
|
192
|
+
bottom_labels = set(self.labels[-1, 0::2]) - {0}
|
|
193
|
+
return np.array(sorted(top_labels & bottom_labels), dtype=np.int64)
|
|
194
|
+
|
|
195
|
+
def spans(self):
|
|
196
|
+
"""Whether the current realization has a top-to-bottom spanning cluster."""
|
|
197
|
+
return len(self.spanning_labels()) > 0
|
|
198
|
+
|
|
199
|
+
def _real_labels(self):
|
|
200
|
+
"""Cluster labels restricted to real (non-bond-auxiliary) lattice sites."""
|
|
201
|
+
return self.labels if self.mode == "site" else self.labels[0::2, 0::2]
|
|
202
|
+
|
|
203
|
+
def largest_cluster_size(self):
|
|
204
|
+
"""Number of (real) sites in the largest cluster of the current realization."""
|
|
205
|
+
sizes = self.cluster_size_distribution()
|
|
206
|
+
return int(sizes.max()) if sizes.size else 0
|
|
207
|
+
|
|
208
|
+
def cluster_size_distribution(self):
|
|
209
|
+
"""Sizes of every cluster in the current realization.
|
|
210
|
+
|
|
211
|
+
At the percolation threshold, the number of clusters of size ``s``
|
|
212
|
+
follows a power law :math:`n_s \\sim s^{-\\tau}` with the Fisher
|
|
213
|
+
exponent :math:`\\tau = 187/91 \\approx 2.055` for 2D percolation --
|
|
214
|
+
the geometric analogue of the divergent susceptibility seen at a
|
|
215
|
+
thermal critical point.
|
|
216
|
+
|
|
217
|
+
Returns
|
|
218
|
+
-------
|
|
219
|
+
ndarray
|
|
220
|
+
Cluster sizes, in sites, one entry per cluster (including the
|
|
221
|
+
spanning cluster if one exists).
|
|
222
|
+
"""
|
|
223
|
+
real_labels = self._real_labels()
|
|
224
|
+
labels = real_labels[real_labels != 0]
|
|
225
|
+
if labels.size == 0:
|
|
226
|
+
return np.array([], dtype=np.int64)
|
|
227
|
+
_, counts = np.unique(labels, return_counts=True)
|
|
228
|
+
return counts
|
|
229
|
+
|
|
230
|
+
def spanning_probability(self, p_values, n_trials=100):
|
|
231
|
+
"""Monte Carlo estimate of the spanning probability :math:`P_{\\text{span}}(p)`.
|
|
232
|
+
|
|
233
|
+
For each probability in ``p_values``, generates ``n_trials``
|
|
234
|
+
independent realizations and reports the fraction that contain a
|
|
235
|
+
top-to-bottom spanning cluster. As :math:`L \\to \\infty` this curve
|
|
236
|
+
sharpens into a step function at :math:`p_c`.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
p_values : array_like
|
|
241
|
+
Occupation probabilities to sample.
|
|
242
|
+
n_trials : int, default=100
|
|
243
|
+
Independent realizations per probability.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
p_values : ndarray
|
|
248
|
+
P_span : ndarray
|
|
249
|
+
Estimated spanning probability at each ``p``.
|
|
250
|
+
|
|
251
|
+
Examples
|
|
252
|
+
--------
|
|
253
|
+
>>> perc = Percolation2D(L=20, seed=0)
|
|
254
|
+
>>> p, P = perc.spanning_probability([0.3, 0.593, 0.8], n_trials=20)
|
|
255
|
+
>>> bool(P[0] < P[2])
|
|
256
|
+
True
|
|
257
|
+
"""
|
|
258
|
+
p_values = np.asarray(p_values, dtype=np.float64)
|
|
259
|
+
P_span = np.empty(len(p_values))
|
|
260
|
+
for k, p in enumerate(p_values):
|
|
261
|
+
hits = 0
|
|
262
|
+
for _ in range(n_trials):
|
|
263
|
+
self.generate(p)
|
|
264
|
+
hits += self.spans()
|
|
265
|
+
P_span[k] = hits / n_trials
|
|
266
|
+
return p_values, P_span
|
|
267
|
+
|
|
268
|
+
def fractal_dimension(self, n_trials=20):
|
|
269
|
+
"""Estimate the fractal dimension of the largest cluster at :math:`p = p_c`.
|
|
270
|
+
|
|
271
|
+
A single lattice size gives no direct mass-versus-``L`` scaling, so
|
|
272
|
+
instead this estimates :math:`d_f` from the mass-radius relation of
|
|
273
|
+
the largest cluster in each realization,
|
|
274
|
+
:math:`d_f = \\log(\\text{cluster size}) / \\log(R_g)`, where
|
|
275
|
+
:math:`R_g` is the cluster's radius of gyration, averaged over
|
|
276
|
+
trials. The 2D percolation universality class predicts
|
|
277
|
+
:math:`d_f = 91/48 \\approx 1.896`.
|
|
278
|
+
|
|
279
|
+
Parameters
|
|
280
|
+
----------
|
|
281
|
+
n_trials : int, default=20
|
|
282
|
+
Number of critical realizations to average over.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
float
|
|
287
|
+
Estimated fractal dimension.
|
|
288
|
+
"""
|
|
289
|
+
dims = []
|
|
290
|
+
for _ in range(n_trials):
|
|
291
|
+
self.generate(self.p_c)
|
|
292
|
+
real_labels = self._real_labels()
|
|
293
|
+
labels = real_labels[real_labels != 0]
|
|
294
|
+
if labels.size == 0:
|
|
295
|
+
continue
|
|
296
|
+
values, counts = np.unique(labels, return_counts=True)
|
|
297
|
+
biggest = values[np.argmax(counts)]
|
|
298
|
+
ii, jj = np.nonzero(real_labels == biggest)
|
|
299
|
+
size = len(ii)
|
|
300
|
+
if size < 4:
|
|
301
|
+
continue
|
|
302
|
+
Rg = np.sqrt(np.mean((ii - ii.mean()) ** 2 + (jj - jj.mean()) ** 2))
|
|
303
|
+
if Rg > 0:
|
|
304
|
+
dims.append(np.log(size) / np.log(Rg))
|
|
305
|
+
return float(np.mean(dims)) if dims else float("nan")
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Random walks: diffusion, the Einstein relation, and the central limit theorem.
|
|
2
|
+
|
|
3
|
+
The random walk is arguably the most foundational model in statistical
|
|
4
|
+
physics: it underlies Einstein's 1905 explanation of Brownian motion (which
|
|
5
|
+
provided the first direct evidence for the atomic hypothesis), and its
|
|
6
|
+
Gaussian long-time limit is the simplest nontrivial instance of the central
|
|
7
|
+
limit theorem, whose universality is itself the deep reason so many
|
|
8
|
+
different microscopic models flow to the same few macroscopic diffusion
|
|
9
|
+
laws.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from physicskit.statphys.core.monte_carlo import seed_numba_random
|
|
17
|
+
from physicskit.statphys.core.random_walk_engine import simulate_gaussian_walks, simulate_lattice_walks
|
|
18
|
+
|
|
19
|
+
__all__ = ["RandomWalk"]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RandomWalk:
|
|
23
|
+
"""An ensemble of independent random walkers used to study diffusion.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
n_walkers : int, default=500
|
|
28
|
+
Number of independent walkers in the ensemble.
|
|
29
|
+
n_steps : int, default=1000
|
|
30
|
+
Number of steps each walker takes.
|
|
31
|
+
dim : int, default=2
|
|
32
|
+
Spatial dimension.
|
|
33
|
+
kind : {"lattice", "gaussian"}, default="lattice"
|
|
34
|
+
``"lattice"`` walkers take unit steps along a randomly chosen axis
|
|
35
|
+
(a simple-cubic lattice walk); ``"gaussian"`` walkers take
|
|
36
|
+
continuous, independent Gaussian steps in every dimension at every
|
|
37
|
+
time step (a discrete-time model of Brownian motion).
|
|
38
|
+
step_std : float, default=1.0
|
|
39
|
+
Per-axis standard deviation of a single step. Only used for
|
|
40
|
+
``kind="gaussian"``.
|
|
41
|
+
seed : int, optional
|
|
42
|
+
Seed for reproducible trajectories.
|
|
43
|
+
|
|
44
|
+
Attributes
|
|
45
|
+
----------
|
|
46
|
+
trajectories : ndarray of shape (n_steps + 1, n_walkers, dim) or None
|
|
47
|
+
Walker positions at every time, populated by :meth:`run`.
|
|
48
|
+
|
|
49
|
+
Examples
|
|
50
|
+
--------
|
|
51
|
+
>>> walk = RandomWalk(n_walkers=200, n_steps=200, dim=2, seed=0)
|
|
52
|
+
>>> t, msd = walk.mean_squared_displacement()
|
|
53
|
+
>>> bool(msd[-1] > msd[10])
|
|
54
|
+
True
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, n_walkers=500, n_steps=1000, dim=2, kind="lattice", step_std=1.0, seed=None):
|
|
58
|
+
self.n_walkers = n_walkers
|
|
59
|
+
self.n_steps = n_steps
|
|
60
|
+
self.dim = dim
|
|
61
|
+
self.kind = kind
|
|
62
|
+
self.step_std = step_std
|
|
63
|
+
if seed is not None:
|
|
64
|
+
seed_numba_random(seed)
|
|
65
|
+
self.trajectories = None
|
|
66
|
+
|
|
67
|
+
def run(self):
|
|
68
|
+
"""Simulate every walker's full trajectory.
|
|
69
|
+
|
|
70
|
+
Returns
|
|
71
|
+
-------
|
|
72
|
+
ndarray of shape (n_steps + 1, n_walkers, dim)
|
|
73
|
+
The same array stored in :attr:`trajectories`.
|
|
74
|
+
"""
|
|
75
|
+
if self.kind == "lattice":
|
|
76
|
+
self.trajectories = simulate_lattice_walks(self.n_walkers, self.n_steps, self.dim).astype(np.float64)
|
|
77
|
+
elif self.kind == "gaussian":
|
|
78
|
+
self.trajectories = simulate_gaussian_walks(self.n_walkers, self.n_steps, self.dim, self.step_std)
|
|
79
|
+
else:
|
|
80
|
+
raise ValueError("kind must be 'lattice' or 'gaussian'")
|
|
81
|
+
return self.trajectories
|
|
82
|
+
|
|
83
|
+
def mean_squared_displacement(self):
|
|
84
|
+
"""Ensemble-averaged mean squared displacement :math:`\\text{MSD}(t) = \\langle |r(t)|^2 \\rangle`.
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
t : ndarray of shape (n_steps + 1,)
|
|
89
|
+
Time (in steps), starting at 0.
|
|
90
|
+
msd : ndarray of shape (n_steps + 1,)
|
|
91
|
+
Mean squared displacement from the (shared) origin at each time.
|
|
92
|
+
"""
|
|
93
|
+
if self.trajectories is None:
|
|
94
|
+
self.run()
|
|
95
|
+
disp2 = np.sum(self.trajectories**2, axis=2)
|
|
96
|
+
msd = disp2.mean(axis=1)
|
|
97
|
+
t = np.arange(self.n_steps + 1)
|
|
98
|
+
return t, msd
|
|
99
|
+
|
|
100
|
+
def diffusion_coefficient(self):
|
|
101
|
+
"""Estimate the diffusion coefficient D via the Einstein relation.
|
|
102
|
+
|
|
103
|
+
Fits :math:`\\text{MSD}(t) = 2 d D t` (a line through the origin,
|
|
104
|
+
least-squares) to the ensemble MSD, where :math:`d` is
|
|
105
|
+
:attr:`dim`.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
float
|
|
110
|
+
Estimated diffusion coefficient.
|
|
111
|
+
"""
|
|
112
|
+
t, msd = self.mean_squared_displacement()
|
|
113
|
+
slope = np.sum(t[1:] * msd[1:]) / np.sum(t[1:] ** 2)
|
|
114
|
+
return float(slope / (2.0 * self.dim))
|
|
115
|
+
|
|
116
|
+
def final_displacement_histogram(self, axis=0, bins=40):
|
|
117
|
+
"""Density histogram of the final-time displacement along one axis.
|
|
118
|
+
|
|
119
|
+
A demonstration of the central limit theorem: regardless of the
|
|
120
|
+
(highly non-Gaussian) single-step distribution, the sum of many
|
|
121
|
+
independent steps approaches a Gaussian.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
axis : int, default=0
|
|
126
|
+
Which spatial axis to histogram.
|
|
127
|
+
bins : int, default=40
|
|
128
|
+
Number of histogram bins.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
centers : ndarray of shape (bins,)
|
|
133
|
+
Bin center positions.
|
|
134
|
+
density : ndarray of shape (bins,)
|
|
135
|
+
Probability density in each bin.
|
|
136
|
+
"""
|
|
137
|
+
if self.trajectories is None:
|
|
138
|
+
self.run()
|
|
139
|
+
final = self.trajectories[-1, :, axis]
|
|
140
|
+
density, edges = np.histogram(final, bins=bins, density=True)
|
|
141
|
+
centers = 0.5 * (edges[:-1] + edges[1:])
|
|
142
|
+
return centers, density
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Real-space renormalization group: Kadanoff block-spin coarse-graining.
|
|
2
|
+
|
|
3
|
+
Kadanoff's block-spin construction groups an Ising lattice into
|
|
4
|
+
:math:`b \\times b` blocks and replaces each block by a single effective spin
|
|
5
|
+
(here, majority rule with ``b = 2``), producing a new, smaller lattice at the
|
|
6
|
+
same nominal temperature. Iterating this map is a real-space renormalization
|
|
7
|
+
group transformation: starting configurations flow toward one of the
|
|
8
|
+
theory's fixed points -- a fully ordered state at :math:`T \\ll T_c`, a fully
|
|
9
|
+
disordered state at :math:`T \\gg T_c`, and a self-similar, scale-invariant
|
|
10
|
+
critical configuration at :math:`T = T_c`, where the coarse-grained lattice
|
|
11
|
+
looks statistically identical to the original.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
|
|
18
|
+
from physicskit.statphys.chapters.ising_lattice import Ising2D
|
|
19
|
+
|
|
20
|
+
__all__ = ["BlockSpinRG"]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BlockSpinRG:
|
|
24
|
+
"""Iterated 2x2 majority-rule block-spin coarse-graining of an Ising configuration.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
L : int, default=64
|
|
29
|
+
Linear size of the initial lattice. Should be a power of 2 times the
|
|
30
|
+
largest block factor you intend to reach, e.g. ``L=64`` supports up
|
|
31
|
+
to 6 coarse-graining steps.
|
|
32
|
+
T : float, default=None
|
|
33
|
+
Temperature at which to equilibrate the initial configuration, in
|
|
34
|
+
units of ``J/kB``. Defaults to the exact critical temperature
|
|
35
|
+
(``Ising2D.T_C``) if not given, i.e. the critical fixed point.
|
|
36
|
+
J : float, default=1.0
|
|
37
|
+
Ising coupling constant used to equilibrate the initial
|
|
38
|
+
configuration.
|
|
39
|
+
n_equil_sweeps : int, default=500
|
|
40
|
+
Wolff cluster updates used to equilibrate the initial fine-grained
|
|
41
|
+
configuration at temperature ``T`` before coarse-graining begins.
|
|
42
|
+
seed : int, optional
|
|
43
|
+
Seed for the initial equilibration and for majority-rule tie
|
|
44
|
+
breaking.
|
|
45
|
+
|
|
46
|
+
Attributes
|
|
47
|
+
----------
|
|
48
|
+
initial_spins : ndarray of shape (L, L)
|
|
49
|
+
The equilibrated fine-grained configuration coarse-graining starts
|
|
50
|
+
from.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, L=64, T=None, J=1.0, n_equil_sweeps=500, seed=None):
|
|
54
|
+
self.L = L
|
|
55
|
+
self.J = J
|
|
56
|
+
self._rng = np.random.default_rng(seed)
|
|
57
|
+
|
|
58
|
+
model = Ising2D(L=L, J=J, seed=seed)
|
|
59
|
+
self.T = model.T_C if T is None else T
|
|
60
|
+
beta = 1.0 / self.T if self.T > 0 else np.inf
|
|
61
|
+
if np.isfinite(beta):
|
|
62
|
+
# Wolff cluster updates equilibrate far more reliably than single-spin
|
|
63
|
+
# Metropolis: at low T, Metropolis dynamics from a random start undergoes
|
|
64
|
+
# slow domain-wall coarsening that a modest sweep budget cannot outrun.
|
|
65
|
+
model.sweep(beta, algorithm="wolff", n_sweeps=n_equil_sweeps)
|
|
66
|
+
self.initial_spins = model.spins.copy()
|
|
67
|
+
|
|
68
|
+
def coarse_grain_step(self, spins):
|
|
69
|
+
"""Apply one 2x2 majority-rule block-spin transformation.
|
|
70
|
+
|
|
71
|
+
Each non-overlapping :math:`2 \\times 2` block of the input is
|
|
72
|
+
replaced by a single spin equal to the sign of the block's sum; an
|
|
73
|
+
exact tie (sum of zero) is broken by an independent fair coin flip.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
spins : ndarray of shape (L, L)
|
|
78
|
+
Input configuration. ``L`` must be even.
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
ndarray of shape (L // 2, L // 2)
|
|
83
|
+
Coarse-grained spin configuration.
|
|
84
|
+
"""
|
|
85
|
+
L = spins.shape[0]
|
|
86
|
+
if L % 2 != 0:
|
|
87
|
+
raise ValueError("lattice size must be even to block by 2x2")
|
|
88
|
+
blocks = spins.reshape(L // 2, 2, L // 2, 2).sum(axis=(1, 3))
|
|
89
|
+
new_spins = np.sign(blocks).astype(np.int64)
|
|
90
|
+
ties = new_spins == 0
|
|
91
|
+
if np.any(ties):
|
|
92
|
+
coin = self._rng.choice(np.array([-1, 1], dtype=np.int64), size=ties.sum())
|
|
93
|
+
new_spins[ties] = coin
|
|
94
|
+
return new_spins
|
|
95
|
+
|
|
96
|
+
def iterate(self, n_steps=None):
|
|
97
|
+
"""Repeatedly coarse-grain the initial configuration.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
n_steps : int, optional
|
|
102
|
+
Number of coarse-graining steps. Defaults to
|
|
103
|
+
``floor(log2(L)) - 1``, i.e. as many as keep the lattice at least
|
|
104
|
+
:math:`2 \\times 2`.
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
list of ndarray
|
|
109
|
+
The sequence of configurations
|
|
110
|
+
``[initial_spins, step_1, step_2, ...]``, of shrinking size
|
|
111
|
+
:math:`L, L/2, L/4, \\ldots`
|
|
112
|
+
|
|
113
|
+
Examples
|
|
114
|
+
--------
|
|
115
|
+
>>> rg = BlockSpinRG(L=16, T=100.0, seed=0)
|
|
116
|
+
>>> grids = rg.iterate(n_steps=3)
|
|
117
|
+
>>> [g.shape[0] for g in grids]
|
|
118
|
+
[16, 8, 4, 2]
|
|
119
|
+
"""
|
|
120
|
+
if n_steps is None:
|
|
121
|
+
n_steps = int(np.floor(np.log2(self.L))) - 1
|
|
122
|
+
grids = [self.initial_spins]
|
|
123
|
+
current = self.initial_spins
|
|
124
|
+
for _ in range(n_steps):
|
|
125
|
+
current = self.coarse_grain_step(current)
|
|
126
|
+
grids.append(current)
|
|
127
|
+
return grids
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
def order_parameter(spins):
|
|
131
|
+
"""Magnitude of the mean magnetization per site, :math:`|\\langle s \\rangle|`.
|
|
132
|
+
|
|
133
|
+
A convenient scalar summary of a grid in the RG flow: it drifts to
|
|
134
|
+
``1`` under iteration when the starting temperature is below
|
|
135
|
+
:math:`T_c` (flow to the ordered fixed point), to ``0`` when above
|
|
136
|
+
(flow to the disordered fixed point), and hovers at an intermediate,
|
|
137
|
+
scale-invariant value at :math:`T_c`.
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
spins : ndarray
|
|
142
|
+
A spin configuration.
|
|
143
|
+
|
|
144
|
+
Returns
|
|
145
|
+
-------
|
|
146
|
+
float
|
|
147
|
+
"""
|
|
148
|
+
return float(np.abs(spins.mean()))
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""The Bak-Tang-Wiesenfeld sandpile: self-organized criticality.
|
|
2
|
+
|
|
3
|
+
Ordinary critical phenomena require fine-tuning a parameter (temperature,
|
|
4
|
+
occupation probability) to a special value. Bak, Tang, and Wiesenfeld's 1987
|
|
5
|
+
sandpile model was the first example of a system that tunes *itself* to a
|
|
6
|
+
critical state through its own slow-driving, fast-relaxation dynamics --
|
|
7
|
+
"self-organized criticality" (SOC) -- and was proposed as a generic
|
|
8
|
+
explanation for the ubiquity of power laws in nature (earthquakes, forest
|
|
9
|
+
fires, neural avalanches), without needing anyone to tune anything.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
|
|
16
|
+
from physicskit.statphys.core.sandpile_engine import topple_to_stability
|
|
17
|
+
|
|
18
|
+
__all__ = ["BTWSandpile"]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BTWSandpile:
|
|
22
|
+
"""The Bak-Tang-Wiesenfeld sandpile on an L x L grid with open (absorbing) boundaries.
|
|
23
|
+
|
|
24
|
+
Grains are added one at a time to a chosen (or random) site; whenever a
|
|
25
|
+
site's height reaches the toppling threshold, it topples, distributing
|
|
26
|
+
one grain to each orthogonal neighbor (grains that fall off the grid's
|
|
27
|
+
edge are permanently lost). A single added grain can trigger a chain
|
|
28
|
+
reaction of topplings -- an avalanche -- whose size has no
|
|
29
|
+
characteristic scale once the pile has self-organized to its critical
|
|
30
|
+
state.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
L : int, default=64
|
|
35
|
+
Linear grid size.
|
|
36
|
+
threshold : int, default=4
|
|
37
|
+
Toppling threshold (4 is the standard value on the square lattice).
|
|
38
|
+
seed : int, optional
|
|
39
|
+
Seed for the random drop-site sequence.
|
|
40
|
+
|
|
41
|
+
Attributes
|
|
42
|
+
----------
|
|
43
|
+
heights : ndarray of shape (L, L)
|
|
44
|
+
Current pile heights; always ``< threshold`` everywhere immediately
|
|
45
|
+
after a call to :meth:`add_grain` or :meth:`run`.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, L=64, threshold=4, seed=None):
|
|
49
|
+
self.L = L
|
|
50
|
+
self.threshold = threshold
|
|
51
|
+
self._rng = np.random.default_rng(seed)
|
|
52
|
+
self.heights = np.zeros((L, L), dtype=np.int64)
|
|
53
|
+
|
|
54
|
+
def add_grain(self, site=None):
|
|
55
|
+
"""Add one grain and relax the pile to stability.
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
site : tuple of int, optional
|
|
60
|
+
``(i, j)`` grid coordinates to drop the grain at. A uniformly
|
|
61
|
+
random site is chosen if not given.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
int
|
|
66
|
+
Avalanche size: the number of individual topplings triggered by
|
|
67
|
+
this grain (``0`` if it did not trigger any toppling).
|
|
68
|
+
"""
|
|
69
|
+
if site is None:
|
|
70
|
+
i = self._rng.integers(0, self.L)
|
|
71
|
+
j = self._rng.integers(0, self.L)
|
|
72
|
+
else:
|
|
73
|
+
i, j = site
|
|
74
|
+
self.heights[i, j] += 1
|
|
75
|
+
return topple_to_stability(self.heights, self.threshold)
|
|
76
|
+
|
|
77
|
+
def run(self, n_grains, warmup=0):
|
|
78
|
+
"""Drop many grains at random sites, recording each one's avalanche size.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
n_grains : int
|
|
83
|
+
Number of grains to add and record after the warmup period.
|
|
84
|
+
warmup : int, default=0
|
|
85
|
+
Grains added first, to let the pile reach its self-organized
|
|
86
|
+
critical state, whose avalanche sizes are discarded.
|
|
87
|
+
|
|
88
|
+
Returns
|
|
89
|
+
-------
|
|
90
|
+
ndarray of shape (n_grains,), dtype int64
|
|
91
|
+
Avalanche size triggered by each recorded grain.
|
|
92
|
+
|
|
93
|
+
Examples
|
|
94
|
+
--------
|
|
95
|
+
>>> pile = BTWSandpile(L=20, seed=0)
|
|
96
|
+
>>> sizes = pile.run(n_grains=500, warmup=500)
|
|
97
|
+
>>> sizes.shape
|
|
98
|
+
(500,)
|
|
99
|
+
"""
|
|
100
|
+
for _ in range(warmup):
|
|
101
|
+
self.add_grain()
|
|
102
|
+
sizes = np.empty(n_grains, dtype=np.int64)
|
|
103
|
+
for k in range(n_grains):
|
|
104
|
+
sizes[k] = self.add_grain()
|
|
105
|
+
return sizes
|
|
106
|
+
|
|
107
|
+
def total_grains(self):
|
|
108
|
+
"""Total number of grains currently on the pile."""
|
|
109
|
+
return int(self.heights.sum())
|