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,557 @@
|
|
|
1
|
+
"""Discrete chaotic maps: the Standard Map, the Henon Map, the Baker's Map, and the Logistic Map."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numba import njit
|
|
7
|
+
from numpy.typing import NDArray
|
|
8
|
+
|
|
9
|
+
from physicskit.chaos.core.base_system import DiscreteMap
|
|
10
|
+
from physicskit.chaos.exceptions import InvalidParameterError
|
|
11
|
+
|
|
12
|
+
TWO_PI: float = 2.0 * np.pi
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@njit(cache=True)
|
|
16
|
+
def _standard_map_step(theta: float, p: float, k: float) -> tuple[float, float]:
|
|
17
|
+
"""Single Chirikov-Taylor standard map iteration.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
theta : float
|
|
22
|
+
Current angle, in radians.
|
|
23
|
+
p : float
|
|
24
|
+
Current momentum.
|
|
25
|
+
k : float
|
|
26
|
+
Kick strength.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
theta_new, p_new : float
|
|
31
|
+
Next angle and momentum, both wrapped into ``[0, 2*pi)``.
|
|
32
|
+
"""
|
|
33
|
+
p_new = (p + k * np.sin(theta)) % TWO_PI
|
|
34
|
+
theta_new = (theta + p_new) % TWO_PI
|
|
35
|
+
return theta_new, p_new
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@njit(cache=True, nogil=True)
|
|
39
|
+
def _standard_map_trajectory(theta0: float, p0: float, k: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
40
|
+
"""Iterate the standard map ``n_iter`` times from ``(theta0, p0)``.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
theta0 : float
|
|
45
|
+
Initial angle, in radians.
|
|
46
|
+
p0 : float
|
|
47
|
+
Initial momentum.
|
|
48
|
+
k : float
|
|
49
|
+
Kick strength.
|
|
50
|
+
n_iter : int
|
|
51
|
+
Number of iterations.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
thetas, ps : ndarray of float, shape (n_iter + 1,)
|
|
56
|
+
Angle and momentum at each iteration, including the initial values.
|
|
57
|
+
"""
|
|
58
|
+
thetas = np.empty(n_iter + 1)
|
|
59
|
+
ps = np.empty(n_iter + 1)
|
|
60
|
+
thetas[0], ps[0] = theta0, p0
|
|
61
|
+
theta, p = theta0, p0
|
|
62
|
+
for i in range(n_iter):
|
|
63
|
+
theta, p = _standard_map_step(theta, p, k)
|
|
64
|
+
thetas[i + 1] = theta
|
|
65
|
+
ps[i + 1] = p
|
|
66
|
+
return thetas, ps
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class StandardMap(DiscreteMap):
|
|
70
|
+
"""The Chirikov-Taylor standard map on the ``(theta, p)`` cylinder.
|
|
71
|
+
|
|
72
|
+
``k=0`` is integrable; chaos onset is around ``k~1``, with global chaos
|
|
73
|
+
for ``k >~ 4-5``.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
k : float, default 1.0
|
|
78
|
+
Kick strength.
|
|
79
|
+
|
|
80
|
+
Attributes
|
|
81
|
+
----------
|
|
82
|
+
k : float
|
|
83
|
+
Kick strength.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
#: State dimension, always 2. State is ``(theta, p)``.
|
|
87
|
+
dim = 2
|
|
88
|
+
|
|
89
|
+
def __init__(self, k: float = 1.0):
|
|
90
|
+
self.k = float(k)
|
|
91
|
+
|
|
92
|
+
def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
93
|
+
"""Advance ``(theta, p)`` by one map iteration.
|
|
94
|
+
|
|
95
|
+
Parameters
|
|
96
|
+
----------
|
|
97
|
+
state : array_like of float, shape (2,)
|
|
98
|
+
Current state ``(theta, p)``.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
ndarray of float, shape (2,)
|
|
103
|
+
Next state ``(theta, p)``.
|
|
104
|
+
"""
|
|
105
|
+
theta, p = _standard_map_step(float(state[0]), float(state[1]), self.k)
|
|
106
|
+
return np.array([theta, p])
|
|
107
|
+
|
|
108
|
+
def initial_state(self) -> NDArray[np.float64]:
|
|
109
|
+
"""Default initial condition ``(0.1, 0.1)``.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
ndarray of float, shape (2,)
|
|
114
|
+
"""
|
|
115
|
+
return np.array([0.1, 0.1])
|
|
116
|
+
|
|
117
|
+
def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
|
|
118
|
+
"""Iterate the map ``n_iter`` times starting from ``state0``.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
state0 : array_like of float, shape (2,), optional
|
|
123
|
+
Initial state ``(theta, p)``; defaults to :meth:`initial_state`.
|
|
124
|
+
n_iter : int, default 1000
|
|
125
|
+
Number of iterations.
|
|
126
|
+
|
|
127
|
+
Returns
|
|
128
|
+
-------
|
|
129
|
+
ndarray of float, shape (n_iter + 1, 2)
|
|
130
|
+
State at each iteration, including ``state0`` as row 0.
|
|
131
|
+
"""
|
|
132
|
+
state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
|
|
133
|
+
thetas, ps = _standard_map_trajectory(float(state0[0]), float(state0[1]), self.k, n_iter)
|
|
134
|
+
return np.column_stack([thetas, ps])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@njit(cache=True)
|
|
138
|
+
def _henon_map_step(x: float, y: float, a: float, b: float) -> tuple[float, float]:
|
|
139
|
+
"""Single Henon map iteration.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
x, y : float
|
|
144
|
+
Current state.
|
|
145
|
+
a, b : float
|
|
146
|
+
Map parameters.
|
|
147
|
+
|
|
148
|
+
Returns
|
|
149
|
+
-------
|
|
150
|
+
x_new, y_new : float
|
|
151
|
+
Next state.
|
|
152
|
+
"""
|
|
153
|
+
return 1.0 - a * x * x + y, b * x
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@njit(cache=True, nogil=True)
|
|
157
|
+
def _henon_map_trajectory(x0: float, y0: float, a: float, b: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
158
|
+
"""Iterate the Henon map ``n_iter`` times from ``(x0, y0)``.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
x0, y0 : float
|
|
163
|
+
Initial state.
|
|
164
|
+
a, b : float
|
|
165
|
+
Map parameters.
|
|
166
|
+
n_iter : int
|
|
167
|
+
Number of iterations.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
xs, ys : ndarray of float, shape (n_iter + 1,)
|
|
172
|
+
State components at each iteration, including the initial values.
|
|
173
|
+
"""
|
|
174
|
+
xs = np.empty(n_iter + 1)
|
|
175
|
+
ys = np.empty(n_iter + 1)
|
|
176
|
+
xs[0], ys[0] = x0, y0
|
|
177
|
+
x, y = x0, y0
|
|
178
|
+
for i in range(n_iter):
|
|
179
|
+
x, y = _henon_map_step(x, y, a, b)
|
|
180
|
+
xs[i + 1] = x
|
|
181
|
+
ys[i + 1] = y
|
|
182
|
+
return xs, ys
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class HenonMap(DiscreteMap):
|
|
186
|
+
"""The Henon map ``x' = 1 - a*x^2 + y, y' = b*x``.
|
|
187
|
+
|
|
188
|
+
The classic chaotic parameters are ``a=1.4, b=0.3``.
|
|
189
|
+
|
|
190
|
+
Parameters
|
|
191
|
+
----------
|
|
192
|
+
a : float, default 1.4
|
|
193
|
+
Map parameter.
|
|
194
|
+
b : float, default 0.3
|
|
195
|
+
Map parameter.
|
|
196
|
+
|
|
197
|
+
Attributes
|
|
198
|
+
----------
|
|
199
|
+
a, b : float
|
|
200
|
+
Map parameters.
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
#: State dimension, always 2. State is ``(x, y)``.
|
|
204
|
+
dim = 2
|
|
205
|
+
|
|
206
|
+
def __init__(self, a: float = 1.4, b: float = 0.3):
|
|
207
|
+
self.a = float(a)
|
|
208
|
+
self.b = float(b)
|
|
209
|
+
|
|
210
|
+
def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
211
|
+
"""Advance ``(x, y)`` by one map iteration.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
state : array_like of float, shape (2,)
|
|
216
|
+
Current state ``(x, y)``.
|
|
217
|
+
|
|
218
|
+
Returns
|
|
219
|
+
-------
|
|
220
|
+
ndarray of float, shape (2,)
|
|
221
|
+
Next state ``(x, y)``.
|
|
222
|
+
"""
|
|
223
|
+
x, y = _henon_map_step(float(state[0]), float(state[1]), self.a, self.b)
|
|
224
|
+
return np.array([x, y])
|
|
225
|
+
|
|
226
|
+
def initial_state(self) -> NDArray[np.float64]:
|
|
227
|
+
"""Default initial condition ``(0, 0)``.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
ndarray of float, shape (2,)
|
|
232
|
+
"""
|
|
233
|
+
return np.array([0.0, 0.0])
|
|
234
|
+
|
|
235
|
+
def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
|
|
236
|
+
"""Iterate the map ``n_iter`` times starting from ``state0``.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
state0 : array_like of float, shape (2,), optional
|
|
241
|
+
Initial state ``(x, y)``; defaults to :meth:`initial_state`.
|
|
242
|
+
n_iter : int, default 1000
|
|
243
|
+
Number of iterations.
|
|
244
|
+
|
|
245
|
+
Returns
|
|
246
|
+
-------
|
|
247
|
+
ndarray of float, shape (n_iter + 1, 2)
|
|
248
|
+
State at each iteration, including ``state0`` as row 0.
|
|
249
|
+
"""
|
|
250
|
+
state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
|
|
251
|
+
xs, ys = _henon_map_trajectory(float(state0[0]), float(state0[1]), self.a, self.b, n_iter)
|
|
252
|
+
return np.column_stack([xs, ys])
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@njit(cache=True)
|
|
256
|
+
def _bakers_map_step(x: float, y: float, alpha: float) -> tuple[float, float]:
|
|
257
|
+
"""Single (generalized) baker's map iteration.
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
x, y : float
|
|
262
|
+
Current state, each in ``[0, 1)``.
|
|
263
|
+
alpha : float
|
|
264
|
+
Cut position, in ``(0, 1)``.
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
x_new, y_new : float
|
|
269
|
+
Next state, each in ``[0, 1)``.
|
|
270
|
+
"""
|
|
271
|
+
if x < alpha:
|
|
272
|
+
return x / alpha, alpha * y
|
|
273
|
+
return (x - alpha) / (1.0 - alpha), alpha + (1.0 - alpha) * y
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@njit(cache=True, nogil=True)
|
|
277
|
+
def _bakers_map_trajectory(x0: float, y0: float, alpha: float, n_iter: int) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
278
|
+
"""Iterate the baker's map ``n_iter`` times from ``(x0, y0)``.
|
|
279
|
+
|
|
280
|
+
Parameters
|
|
281
|
+
----------
|
|
282
|
+
x0, y0 : float
|
|
283
|
+
Initial state, each in ``[0, 1)``.
|
|
284
|
+
alpha : float
|
|
285
|
+
Cut position, in ``(0, 1)``.
|
|
286
|
+
n_iter : int
|
|
287
|
+
Number of iterations.
|
|
288
|
+
|
|
289
|
+
Returns
|
|
290
|
+
-------
|
|
291
|
+
xs, ys : ndarray of float, shape (n_iter + 1,)
|
|
292
|
+
State components at each iteration, including the initial values.
|
|
293
|
+
"""
|
|
294
|
+
xs = np.empty(n_iter + 1)
|
|
295
|
+
ys = np.empty(n_iter + 1)
|
|
296
|
+
xs[0], ys[0] = x0, y0
|
|
297
|
+
x, y = x0, y0
|
|
298
|
+
for i in range(n_iter):
|
|
299
|
+
x, y = _bakers_map_step(x, y, alpha)
|
|
300
|
+
xs[i + 1] = x
|
|
301
|
+
ys[i + 1] = y
|
|
302
|
+
return xs, ys
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class BakersMap(DiscreteMap):
|
|
306
|
+
"""The (generalized) baker's map on the unit square: the textbook model of chaos.
|
|
307
|
+
|
|
308
|
+
The map cuts the unit square ``[0, 1) x [0, 1)`` at ``x = alpha``, stretches
|
|
309
|
+
each piece horizontally back to unit width (contracting it vertically to
|
|
310
|
+
match), and stacks the two pieces -- literally the "stretch, cut, and
|
|
311
|
+
stack" mechanism used to motivate deterministic chaos:
|
|
312
|
+
|
|
313
|
+
.. math::
|
|
314
|
+
|
|
315
|
+
T(x, y) = \\begin{cases}
|
|
316
|
+
(x / \\alpha,\\ \\alpha y) & 0 \\le x < \\alpha \\\\
|
|
317
|
+
((x - \\alpha) / (1 - \\alpha),\\ \\alpha + (1 - \\alpha) y) & \\alpha \\le x < 1
|
|
318
|
+
\\end{cases}
|
|
319
|
+
|
|
320
|
+
Because each branch is affine with Jacobian determinant exactly 1, the map
|
|
321
|
+
is *area-preserving* (unlike the dissipative Henon map) while still being
|
|
322
|
+
uniformly hyperbolic, ergodic, and mixing: a single long orbit fills the
|
|
323
|
+
unit square uniformly and densely, and its Lyapunov exponents are known
|
|
324
|
+
exactly in closed form (see :meth:`lyapunov_exponents`), making it the
|
|
325
|
+
standard textbook example for validating numerical chaos estimators.
|
|
326
|
+
``alpha=0.5`` (the default) recovers the classic symmetric baker's map.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
alpha : float, default 0.5
|
|
331
|
+
Cut position, in ``(0, 1)``; the classic symmetric map has
|
|
332
|
+
``alpha=0.5``.
|
|
333
|
+
|
|
334
|
+
Attributes
|
|
335
|
+
----------
|
|
336
|
+
alpha : float
|
|
337
|
+
Cut position.
|
|
338
|
+
|
|
339
|
+
Raises
|
|
340
|
+
------
|
|
341
|
+
ValueError
|
|
342
|
+
If `alpha` does not satisfy ``0 < alpha < 1``.
|
|
343
|
+
|
|
344
|
+
Notes
|
|
345
|
+
-----
|
|
346
|
+
``x = 0`` is an exact fixed point of the map for *every* `alpha` (since
|
|
347
|
+
``0 / alpha = 0``, always in the first branch). Because each branch is
|
|
348
|
+
expanding, floating-point rounding error is amplified every step, and
|
|
349
|
+
for many choices of `alpha` and the initial condition, a long-enough
|
|
350
|
+
orbit eventually rounds to exactly ``0.0`` in floating point and gets
|
|
351
|
+
permanently trapped there -- silently producing a degenerate, physically
|
|
352
|
+
meaningless trajectory rather than raising an error. This typically
|
|
353
|
+
happens within a few hundred to a few thousand iterations (it depends
|
|
354
|
+
sensitively on `alpha` and the initial condition: it may also never
|
|
355
|
+
happen within a given run). Do not rely on :meth:`trajectory` orbits of
|
|
356
|
+
more than a few hundred iterations to be a faithful sample of the
|
|
357
|
+
invariant measure without first checking the orbit hasn't collapsed;
|
|
358
|
+
:class:`~physicskit.chaos.systems.maps.HenonMap` and continuous systems such as
|
|
359
|
+
:class:`~physicskit.chaos.systems.continuous.Lorenz` do not share this failure
|
|
360
|
+
mode and are better suited to long-trajectory statistics.
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
#: State dimension, always 2. State is ``(x, y)``.
|
|
364
|
+
dim = 2
|
|
365
|
+
|
|
366
|
+
def __init__(self, alpha: float = 0.5):
|
|
367
|
+
if not 0.0 < alpha < 1.0:
|
|
368
|
+
raise InvalidParameterError("alpha must satisfy 0 < alpha < 1")
|
|
369
|
+
self.alpha = float(alpha)
|
|
370
|
+
|
|
371
|
+
def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
372
|
+
"""Advance ``(x, y)`` by one map iteration.
|
|
373
|
+
|
|
374
|
+
Parameters
|
|
375
|
+
----------
|
|
376
|
+
state : array_like of float, shape (2,)
|
|
377
|
+
Current state ``(x, y)``, each in ``[0, 1)``.
|
|
378
|
+
|
|
379
|
+
Returns
|
|
380
|
+
-------
|
|
381
|
+
ndarray of float, shape (2,)
|
|
382
|
+
Next state ``(x, y)``.
|
|
383
|
+
"""
|
|
384
|
+
x, y = _bakers_map_step(float(state[0]), float(state[1]), self.alpha)
|
|
385
|
+
return np.array([x, y])
|
|
386
|
+
|
|
387
|
+
def initial_state(self) -> NDArray[np.float64]:
|
|
388
|
+
"""Default initial condition ``(0.5, 0.5)``.
|
|
389
|
+
|
|
390
|
+
Returns
|
|
391
|
+
-------
|
|
392
|
+
ndarray of float, shape (2,)
|
|
393
|
+
"""
|
|
394
|
+
return np.array([0.5, 0.5])
|
|
395
|
+
|
|
396
|
+
def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
|
|
397
|
+
"""Iterate the map ``n_iter`` times starting from ``state0``.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
state0 : array_like of float, shape (2,), optional
|
|
402
|
+
Initial state ``(x, y)``, each in ``[0, 1)``; defaults to
|
|
403
|
+
:meth:`initial_state`.
|
|
404
|
+
n_iter : int, default 1000
|
|
405
|
+
Number of iterations.
|
|
406
|
+
|
|
407
|
+
Returns
|
|
408
|
+
-------
|
|
409
|
+
ndarray of float, shape (n_iter + 1, 2)
|
|
410
|
+
State at each iteration, including ``state0`` as row 0.
|
|
411
|
+
"""
|
|
412
|
+
state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
|
|
413
|
+
xs, ys = _bakers_map_trajectory(float(state0[0]), float(state0[1]), self.alpha, n_iter)
|
|
414
|
+
return np.column_stack([xs, ys])
|
|
415
|
+
|
|
416
|
+
def lyapunov_exponents(self) -> tuple[float, float]:
|
|
417
|
+
"""Exact Lyapunov exponents of the map, in closed form.
|
|
418
|
+
|
|
419
|
+
The baker's map is piecewise-linear and uniformly hyperbolic: almost
|
|
420
|
+
every orbit spends a fraction `alpha` of its time in the expanding-by
|
|
421
|
+
``1/alpha`` branch and ``1 - alpha`` in the expanding-by
|
|
422
|
+
``1/(1 - alpha)`` branch, so by the ergodic theorem its Lyapunov
|
|
423
|
+
exponent equals the Shannon entropy of that two-symbol Bernoulli
|
|
424
|
+
process -- no numerical estimation needed. This is exactly what makes
|
|
425
|
+
the baker's map useful as a ground truth for validating
|
|
426
|
+
:func:`physicskit.chaos.utils.metrics.lyapunov_exponent_from_divergence` and
|
|
427
|
+
similar numerical estimators.
|
|
428
|
+
|
|
429
|
+
Returns
|
|
430
|
+
-------
|
|
431
|
+
lambda_expanding, lambda_contracting : float
|
|
432
|
+
The two Lyapunov exponents, ``+h`` and ``-h``, where
|
|
433
|
+
``h = -alpha*ln(alpha) - (1-alpha)*ln(1-alpha)`` (``= ln(2)`` for
|
|
434
|
+
the classic symmetric map, ``alpha=0.5``).
|
|
435
|
+
"""
|
|
436
|
+
alpha = self.alpha
|
|
437
|
+
h = -(alpha * np.log(alpha) + (1.0 - alpha) * np.log(1.0 - alpha))
|
|
438
|
+
return h, -h
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
@njit(cache=True)
|
|
442
|
+
def _logistic_map_step(x: float, r: float) -> float:
|
|
443
|
+
"""Single logistic map iteration.
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
x : float
|
|
448
|
+
Current state, in ``[0, 1]``.
|
|
449
|
+
r : float
|
|
450
|
+
Growth rate parameter.
|
|
451
|
+
|
|
452
|
+
Returns
|
|
453
|
+
-------
|
|
454
|
+
float
|
|
455
|
+
Next state.
|
|
456
|
+
"""
|
|
457
|
+
return r * x * (1.0 - x)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
@njit(cache=True, nogil=True)
|
|
461
|
+
def _logistic_map_trajectory(x0: float, r: float, n_iter: int) -> NDArray[np.float64]:
|
|
462
|
+
"""Iterate the logistic map ``n_iter`` times from ``x0``.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
x0 : float
|
|
467
|
+
Initial state, in ``[0, 1]``.
|
|
468
|
+
r : float
|
|
469
|
+
Growth rate parameter.
|
|
470
|
+
n_iter : int
|
|
471
|
+
Number of iterations.
|
|
472
|
+
|
|
473
|
+
Returns
|
|
474
|
+
-------
|
|
475
|
+
ndarray of float, shape (n_iter + 1,)
|
|
476
|
+
State at each iteration, including the initial value.
|
|
477
|
+
"""
|
|
478
|
+
xs = np.empty(n_iter + 1)
|
|
479
|
+
xs[0] = x0
|
|
480
|
+
x = x0
|
|
481
|
+
for i in range(n_iter):
|
|
482
|
+
x = _logistic_map_step(x, r)
|
|
483
|
+
xs[i + 1] = x
|
|
484
|
+
return xs
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
class LogisticMap(DiscreteMap):
|
|
488
|
+
"""The logistic map ``x' = r*x*(1-x)``: the simplest gateway to chaos.
|
|
489
|
+
|
|
490
|
+
Varying the single growth-rate parameter `r` takes this map through the
|
|
491
|
+
complete period-doubling route to chaos: a stable fixed point for
|
|
492
|
+
``r < 3``, then successive period-doubling bifurcations at ``r`` values
|
|
493
|
+
that accumulate geometrically (ratio converging to the universal
|
|
494
|
+
Feigenbaum constant ``delta ~= 4.669``) onto the onset of chaos at
|
|
495
|
+
``r ~= 3.5699``, beyond which the map is chaotic for most (but not all --
|
|
496
|
+
note the periodic windows, the largest at ``r ~= 3.8284``) values of `r`
|
|
497
|
+
up to ``r=4``.
|
|
498
|
+
|
|
499
|
+
Parameters
|
|
500
|
+
----------
|
|
501
|
+
r : float, default 3.9
|
|
502
|
+
Growth rate parameter; interesting values range over ``[0, 4]``.
|
|
503
|
+
|
|
504
|
+
Attributes
|
|
505
|
+
----------
|
|
506
|
+
r : float
|
|
507
|
+
Growth rate parameter.
|
|
508
|
+
"""
|
|
509
|
+
|
|
510
|
+
#: State dimension, always 1. State is ``(x,)``.
|
|
511
|
+
dim = 1
|
|
512
|
+
|
|
513
|
+
def __init__(self, r: float = 3.9):
|
|
514
|
+
self.r = float(r)
|
|
515
|
+
|
|
516
|
+
def step(self, state: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
517
|
+
"""Advance ``x`` by one map iteration.
|
|
518
|
+
|
|
519
|
+
Parameters
|
|
520
|
+
----------
|
|
521
|
+
state : array_like of float, shape (1,)
|
|
522
|
+
Current state ``(x,)``.
|
|
523
|
+
|
|
524
|
+
Returns
|
|
525
|
+
-------
|
|
526
|
+
ndarray of float, shape (1,)
|
|
527
|
+
Next state ``(x,)``.
|
|
528
|
+
"""
|
|
529
|
+
return np.array([_logistic_map_step(float(state[0]), self.r)])
|
|
530
|
+
|
|
531
|
+
def initial_state(self) -> NDArray[np.float64]:
|
|
532
|
+
"""Default initial condition ``(0.5,)``.
|
|
533
|
+
|
|
534
|
+
Returns
|
|
535
|
+
-------
|
|
536
|
+
ndarray of float, shape (1,)
|
|
537
|
+
"""
|
|
538
|
+
return np.array([0.5])
|
|
539
|
+
|
|
540
|
+
def trajectory(self, state0: NDArray[np.float64] | None = None, n_iter: int = 1000) -> NDArray[np.float64]:
|
|
541
|
+
"""Iterate the map ``n_iter`` times starting from ``state0``.
|
|
542
|
+
|
|
543
|
+
Parameters
|
|
544
|
+
----------
|
|
545
|
+
state0 : array_like of float, shape (1,), optional
|
|
546
|
+
Initial state ``(x,)``; defaults to :meth:`initial_state`.
|
|
547
|
+
n_iter : int, default 1000
|
|
548
|
+
Number of iterations.
|
|
549
|
+
|
|
550
|
+
Returns
|
|
551
|
+
-------
|
|
552
|
+
ndarray of float, shape (n_iter + 1, 1)
|
|
553
|
+
State at each iteration, including ``state0`` as row 0.
|
|
554
|
+
"""
|
|
555
|
+
state0 = self.initial_state() if state0 is None else np.asarray(state0, dtype=np.float64)
|
|
556
|
+
xs = _logistic_map_trajectory(float(state0[0]), self.r, n_iter)
|
|
557
|
+
return xs.reshape(-1, 1)
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from physicskit.chaos.systems.continuous import MagneticPendulum, magnetic_pendulum_rhs
|
|
4
|
+
from physicskit.chaos.visualizers.basins import basin_of_attraction
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_basin_of_attraction_output_shapes():
|
|
8
|
+
system = MagneticPendulum()
|
|
9
|
+
xs, ys, labels = basin_of_attraction(
|
|
10
|
+
magnetic_pendulum_rhs,
|
|
11
|
+
system.params,
|
|
12
|
+
x_range=(-2.0, 2.0),
|
|
13
|
+
y_range=(-2.0, 2.0),
|
|
14
|
+
attractors=system.magnet_positions,
|
|
15
|
+
resolution=20,
|
|
16
|
+
dt=0.05,
|
|
17
|
+
n_steps=500,
|
|
18
|
+
)
|
|
19
|
+
assert xs.shape == (20,)
|
|
20
|
+
assert ys.shape == (20,)
|
|
21
|
+
assert labels.shape == (20, 20)
|
|
22
|
+
assert labels.dtype == np.int64
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_basin_of_attraction_labels_are_valid_attractor_indices():
|
|
26
|
+
system = MagneticPendulum()
|
|
27
|
+
_, _, labels = basin_of_attraction(
|
|
28
|
+
magnetic_pendulum_rhs,
|
|
29
|
+
system.params,
|
|
30
|
+
x_range=(-2.0, 2.0),
|
|
31
|
+
y_range=(-2.0, 2.0),
|
|
32
|
+
attractors=system.magnet_positions,
|
|
33
|
+
resolution=20,
|
|
34
|
+
dt=0.05,
|
|
35
|
+
n_steps=500,
|
|
36
|
+
)
|
|
37
|
+
assert np.all(labels >= 0)
|
|
38
|
+
assert np.all(labels < system.magnet_positions.shape[0])
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_basin_of_attraction_finds_every_attractor_with_symmetric_magnets():
|
|
42
|
+
"""With 3 symmetrically-placed magnets and a grid centered on them, every
|
|
43
|
+
magnet should win somewhere on the grid."""
|
|
44
|
+
system = MagneticPendulum()
|
|
45
|
+
_, _, labels = basin_of_attraction(
|
|
46
|
+
magnetic_pendulum_rhs,
|
|
47
|
+
system.params,
|
|
48
|
+
x_range=(-2.0, 2.0),
|
|
49
|
+
y_range=(-2.0, 2.0),
|
|
50
|
+
attractors=system.magnet_positions,
|
|
51
|
+
resolution=60,
|
|
52
|
+
dt=0.05,
|
|
53
|
+
n_steps=500,
|
|
54
|
+
)
|
|
55
|
+
assert set(np.unique(labels).tolist()) == {0, 1, 2}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_basin_of_attraction_point_at_a_magnet_is_labeled_that_magnet():
|
|
59
|
+
"""A grid point starting exactly on top of a magnet (with zero velocity)
|
|
60
|
+
should be classified to that same magnet."""
|
|
61
|
+
system = MagneticPendulum()
|
|
62
|
+
magnet_idx = 0
|
|
63
|
+
magnet = system.magnet_positions[magnet_idx]
|
|
64
|
+
# A tiny grid straddling the magnet position.
|
|
65
|
+
_xs, _ys, labels = basin_of_attraction(
|
|
66
|
+
magnetic_pendulum_rhs,
|
|
67
|
+
system.params,
|
|
68
|
+
x_range=(magnet[0] - 0.01, magnet[0] + 0.01),
|
|
69
|
+
y_range=(magnet[1] - 0.01, magnet[1] + 0.01),
|
|
70
|
+
attractors=system.magnet_positions,
|
|
71
|
+
resolution=3,
|
|
72
|
+
dt=0.05,
|
|
73
|
+
n_steps=500,
|
|
74
|
+
)
|
|
75
|
+
center_label = labels[1, 1]
|
|
76
|
+
assert center_label == magnet_idx
|