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,303 @@
|
|
|
1
|
+
"""Analysis of raw time series with unknown governing equations.
|
|
2
|
+
|
|
3
|
+
Every other Lyapunov/dimension tool in physicskit.chaos works on a
|
|
4
|
+
:class:`~physicskit.chaos.core.base_system.DynamicalSystem` (or a point cloud
|
|
5
|
+
generated from one). This module instead works on a bare, scalar (or
|
|
6
|
+
already-embedded) time series with no known equations of motion -- the
|
|
7
|
+
situation for real experimental or observational data -- via delay-coordinate
|
|
8
|
+
(Takens) embedding, the Rosenstein algorithm for the largest Lyapunov
|
|
9
|
+
exponent, and IAAFT surrogate-data significance testing.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from collections.abc import Callable
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
from numpy.typing import ArrayLike, NDArray
|
|
18
|
+
from scipy.spatial import cKDTree
|
|
19
|
+
from tqdm.auto import tqdm
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def delay_embed(series: ArrayLike, dim: int, tau: int = 1) -> NDArray[np.float64]:
|
|
23
|
+
"""Reconstruct a delay-coordinate (Takens) embedding of a scalar time series.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
series : array_like of float, shape (n,)
|
|
28
|
+
Scalar time series.
|
|
29
|
+
dim : int
|
|
30
|
+
Embedding dimension.
|
|
31
|
+
tau : int, default 1
|
|
32
|
+
Delay, in samples, between successive coordinates.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
ndarray of float, shape (n - (dim - 1) * tau, dim)
|
|
37
|
+
Row ``i`` is ``(series[i], series[i + tau], ..., series[i + (dim-1)*tau])``.
|
|
38
|
+
|
|
39
|
+
Notes
|
|
40
|
+
-----
|
|
41
|
+
Choosing `dim` and `tau` well matters a great deal and is its own
|
|
42
|
+
sub-field; this function does not attempt to choose them for you. Common
|
|
43
|
+
heuristics are the first minimum of the time-delayed mutual information
|
|
44
|
+
for `tau`, and the false-nearest-neighbors method for `dim`.
|
|
45
|
+
"""
|
|
46
|
+
series = np.asarray(series, dtype=np.float64)
|
|
47
|
+
n = series.shape[0] - (dim - 1) * tau
|
|
48
|
+
if n <= 0:
|
|
49
|
+
raise ValueError("series is too short for the requested dim and tau")
|
|
50
|
+
return np.column_stack([series[i * tau : i * tau + n] for i in range(dim)])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _nearest_neighbors_excluding_theiler_window(embedded: NDArray[np.float64], min_tsep: int) -> NDArray[np.int64]:
|
|
54
|
+
"""For each embedded point, find its nearest neighbor at least `min_tsep`
|
|
55
|
+
samples away in time, via a KD-tree (``O(n log n)``) rather than a full
|
|
56
|
+
``O(n^2)`` pairwise distance matrix -- the latter is both slow and, for
|
|
57
|
+
anything beyond a few thousand points, memory-prohibitive (e.g. ~2.9 GB
|
|
58
|
+
of ``float64`` distances for a 19,000-point series).
|
|
59
|
+
"""
|
|
60
|
+
n = embedded.shape[0]
|
|
61
|
+
tree = cKDTree(embedded)
|
|
62
|
+
k = min(n, max(4, 2 * min_tsep + 4))
|
|
63
|
+
_, candidate_idx = tree.query(embedded, k=k)
|
|
64
|
+
candidate_idx = np.atleast_2d(candidate_idx)
|
|
65
|
+
|
|
66
|
+
row_idx = np.arange(n)[:, None]
|
|
67
|
+
valid = np.abs(candidate_idx - row_idx) > min_tsep
|
|
68
|
+
has_valid = valid.any(axis=1)
|
|
69
|
+
first_valid_col = np.argmax(valid, axis=1)
|
|
70
|
+
nearest = candidate_idx[np.arange(n), first_valid_col]
|
|
71
|
+
|
|
72
|
+
# Rare fallback for points whose k nearest neighbors are all within the
|
|
73
|
+
# Theiler window: search directly, excluding just that window.
|
|
74
|
+
for i in np.flatnonzero(~has_valid):
|
|
75
|
+
dists_i = np.linalg.norm(embedded - embedded[i], axis=1)
|
|
76
|
+
dists_i[max(0, i - min_tsep) : i + min_tsep + 1] = np.inf
|
|
77
|
+
nearest[i] = np.argmin(dists_i)
|
|
78
|
+
|
|
79
|
+
return np.asarray(nearest, dtype=np.int64)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def average_log_divergence(
|
|
83
|
+
series: ArrayLike,
|
|
84
|
+
dim: int,
|
|
85
|
+
tau: int = 1,
|
|
86
|
+
min_tsep: int | None = None,
|
|
87
|
+
max_iter: int | None = None,
|
|
88
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
89
|
+
"""Rosenstein's average log nearest-neighbor divergence curve.
|
|
90
|
+
|
|
91
|
+
Embeds `series` (via :func:`delay_embed`), finds each embedded point's
|
|
92
|
+
nearest neighbor (excluding temporally close points, within `min_tsep`,
|
|
93
|
+
to avoid spuriously "finding" a neighbor that is really just the same
|
|
94
|
+
part of the trajectory shifted slightly in time), and tracks how the
|
|
95
|
+
distance to that neighbor grows as both points evolve forward -- the
|
|
96
|
+
time-series analog of :func:`physicskit.chaos.visualizers.divergence.trajectory_divergence`,
|
|
97
|
+
reconstructed from a single observed series with unknown dynamics.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
series : array_like of float, shape (n,)
|
|
102
|
+
Scalar time series.
|
|
103
|
+
dim : int
|
|
104
|
+
Embedding dimension.
|
|
105
|
+
tau : int, default 1
|
|
106
|
+
Embedding delay, in samples.
|
|
107
|
+
min_tsep : int, optional
|
|
108
|
+
Minimum temporal separation (in samples) for a candidate nearest
|
|
109
|
+
neighbor; defaults to `tau`.
|
|
110
|
+
max_iter : int, optional
|
|
111
|
+
Number of forward-evolution steps to track; defaults to
|
|
112
|
+
``n_embedded // 10``.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
steps : ndarray of int, shape (m,)
|
|
117
|
+
Forward-evolution step counts (in samples) with at least one valid
|
|
118
|
+
pair to average over.
|
|
119
|
+
log_divergence : ndarray of float, shape (m,)
|
|
120
|
+
The mean, over all valid pairs, of ``log(||embedded[i+k] -
|
|
121
|
+
embedded[nearest[i]+k]||)`` at each step `k`. Its slope (in the
|
|
122
|
+
linear-looking early region) estimates the largest Lyapunov
|
|
123
|
+
exponent, in units of "per sample"; see :func:`rosenstein_lyapunov`.
|
|
124
|
+
"""
|
|
125
|
+
embedded = delay_embed(series, dim, tau)
|
|
126
|
+
n = embedded.shape[0]
|
|
127
|
+
if min_tsep is None:
|
|
128
|
+
min_tsep = tau
|
|
129
|
+
if max_iter is None:
|
|
130
|
+
max_iter = max(1, n // 10)
|
|
131
|
+
|
|
132
|
+
nearest = _nearest_neighbors_excluding_theiler_window(embedded, min_tsep)
|
|
133
|
+
idx = np.arange(n)
|
|
134
|
+
|
|
135
|
+
steps = []
|
|
136
|
+
log_divergence = []
|
|
137
|
+
for k in range(max_iter):
|
|
138
|
+
valid = (idx + k < n) & (nearest + k < n)
|
|
139
|
+
if not np.any(valid):
|
|
140
|
+
break
|
|
141
|
+
d = np.linalg.norm(embedded[idx[valid] + k] - embedded[nearest[valid] + k], axis=1)
|
|
142
|
+
d = d[d > 0]
|
|
143
|
+
if d.size == 0:
|
|
144
|
+
continue
|
|
145
|
+
steps.append(k)
|
|
146
|
+
log_divergence.append(np.mean(np.log(d)))
|
|
147
|
+
|
|
148
|
+
return np.array(steps), np.array(log_divergence)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def rosenstein_lyapunov(
|
|
152
|
+
series: ArrayLike,
|
|
153
|
+
dim: int,
|
|
154
|
+
tau: int = 1,
|
|
155
|
+
dt: float = 1.0,
|
|
156
|
+
min_tsep: int | None = None,
|
|
157
|
+
max_iter: int | None = None,
|
|
158
|
+
fit_fraction: float = 0.5,
|
|
159
|
+
) -> float:
|
|
160
|
+
"""Estimate the largest Lyapunov exponent of a scalar time series (Rosenstein's algorithm).
|
|
161
|
+
|
|
162
|
+
Unlike every other Lyapunov estimator in physicskit.chaos, this one requires no
|
|
163
|
+
known equations of motion -- only a sampled time series -- making it the
|
|
164
|
+
tool to reach for on real experimental or observational data. Internally
|
|
165
|
+
calls :func:`average_log_divergence` and fits its slope over the first
|
|
166
|
+
`fit_fraction` of the curve, mirroring
|
|
167
|
+
:func:`physicskit.chaos.utils.metrics.lyapunov_exponent_from_divergence`.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
series : array_like of float, shape (n,)
|
|
172
|
+
Scalar time series.
|
|
173
|
+
dim : int
|
|
174
|
+
Embedding dimension.
|
|
175
|
+
tau : int, default 1
|
|
176
|
+
Embedding delay, in samples.
|
|
177
|
+
dt : float, default 1.0
|
|
178
|
+
Time between consecutive samples of `series`, used to convert the
|
|
179
|
+
per-sample slope into a per-time-unit exponent.
|
|
180
|
+
min_tsep : int, optional
|
|
181
|
+
Minimum temporal separation for a candidate nearest neighbor;
|
|
182
|
+
defaults to `tau`.
|
|
183
|
+
max_iter : int, optional
|
|
184
|
+
Number of forward-evolution steps to track; defaults to
|
|
185
|
+
``n_embedded // 10``.
|
|
186
|
+
fit_fraction : float, default 0.5
|
|
187
|
+
Fraction of the (start of the) divergence curve used for the linear
|
|
188
|
+
fit, before it saturates at the attractor's scale.
|
|
189
|
+
|
|
190
|
+
Returns
|
|
191
|
+
-------
|
|
192
|
+
float
|
|
193
|
+
Estimated largest Lyapunov exponent, in units of 1/`dt`.
|
|
194
|
+
"""
|
|
195
|
+
steps, log_divergence = average_log_divergence(series, dim, tau=tau, min_tsep=min_tsep, max_iter=max_iter)
|
|
196
|
+
if steps.size < 2:
|
|
197
|
+
raise ValueError("not enough divergence-curve points to fit a slope")
|
|
198
|
+
n_fit = max(2, int(fit_fraction * steps.size))
|
|
199
|
+
slope, _ = np.polyfit(steps[:n_fit] * dt, log_divergence[:n_fit], 1)
|
|
200
|
+
return float(slope)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def iaaft_surrogate(series: ArrayLike, n_iter: int = 100, seed: int | None = None) -> NDArray[np.float64]:
|
|
204
|
+
"""Generate one IAAFT surrogate of a time series.
|
|
205
|
+
|
|
206
|
+
The Iterative Amplitude-Adjusted Fourier Transform (Schreiber & Schmitz
|
|
207
|
+
1996) produces a surrogate series with (very close to) the same value
|
|
208
|
+
distribution and the same power spectrum as `series`, but with its phase
|
|
209
|
+
relationships randomized -- i.e. a realization of a linear, Gaussian
|
|
210
|
+
stochastic process with the same second-order statistics. Any nonlinear
|
|
211
|
+
structure (including determinism/chaos) present in `series` but absent
|
|
212
|
+
from its surrogates is evidence the series is not just linearly
|
|
213
|
+
correlated noise; see :func:`surrogate_test`.
|
|
214
|
+
|
|
215
|
+
Parameters
|
|
216
|
+
----------
|
|
217
|
+
series : array_like of float, shape (n,)
|
|
218
|
+
Time series to generate a surrogate of.
|
|
219
|
+
n_iter : int, default 100
|
|
220
|
+
Number of alternating spectrum/distribution adjustment iterations.
|
|
221
|
+
seed : int, optional
|
|
222
|
+
Seed for the initial random shuffle, for reproducibility.
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
ndarray of float, shape (n,)
|
|
227
|
+
One IAAFT surrogate realization.
|
|
228
|
+
"""
|
|
229
|
+
series = np.asarray(series, dtype=np.float64)
|
|
230
|
+
n = series.shape[0]
|
|
231
|
+
rng = np.random.default_rng(seed)
|
|
232
|
+
sorted_original = np.sort(series)
|
|
233
|
+
target_amplitude = np.abs(np.fft.rfft(series))
|
|
234
|
+
|
|
235
|
+
surrogate = rng.permutation(series)
|
|
236
|
+
for _ in range(n_iter):
|
|
237
|
+
phases = np.angle(np.fft.rfft(surrogate))
|
|
238
|
+
surrogate = np.fft.irfft(target_amplitude * np.exp(1j * phases), n=n)
|
|
239
|
+
ranks = np.argsort(np.argsort(surrogate))
|
|
240
|
+
surrogate = sorted_original[ranks]
|
|
241
|
+
return surrogate
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def surrogate_test(
|
|
245
|
+
series: ArrayLike,
|
|
246
|
+
statistic_fn: Callable[[NDArray[np.float64]], float],
|
|
247
|
+
n_surrogates: int = 39,
|
|
248
|
+
n_iter: int = 100,
|
|
249
|
+
seed: int | None = None,
|
|
250
|
+
show_progress: bool = False,
|
|
251
|
+
) -> tuple[float, NDArray[np.float64], float]:
|
|
252
|
+
"""Test whether a time series shows structure beyond a linear stochastic process.
|
|
253
|
+
|
|
254
|
+
Computes `statistic_fn` (e.g. :func:`rosenstein_lyapunov`, or
|
|
255
|
+
:func:`physicskit.chaos.utils.dimension.correlation_dimension` wrapped to return
|
|
256
|
+
just its dimension estimate) on `series` and on `n_surrogates` IAAFT
|
|
257
|
+
surrogates (:func:`iaaft_surrogate`), then reports how many standard
|
|
258
|
+
deviations the observed value is from the surrogate distribution. The
|
|
259
|
+
default ``n_surrogates=39`` follows the standard convention (Theiler et
|
|
260
|
+
al. 1992) of giving a one-sided 2.5% significance threshold when
|
|
261
|
+
`statistic_fn` is significantly larger (or smaller) than every surrogate.
|
|
262
|
+
|
|
263
|
+
Parameters
|
|
264
|
+
----------
|
|
265
|
+
series : array_like of float, shape (n,)
|
|
266
|
+
Time series to test.
|
|
267
|
+
statistic_fn : callable
|
|
268
|
+
Function ``statistic_fn(series) -> float`` computing the statistic of
|
|
269
|
+
interest (e.g. a Lyapunov exponent or fractal dimension estimate).
|
|
270
|
+
n_surrogates : int, default 39
|
|
271
|
+
Number of IAAFT surrogates to generate.
|
|
272
|
+
n_iter : int, default 100
|
|
273
|
+
Number of IAAFT iterations per surrogate.
|
|
274
|
+
seed : int, optional
|
|
275
|
+
Seed for surrogate generation, for reproducibility.
|
|
276
|
+
show_progress : bool, default False
|
|
277
|
+
Display a `tqdm` progress bar over surrogates.
|
|
278
|
+
|
|
279
|
+
Returns
|
|
280
|
+
-------
|
|
281
|
+
observed : float
|
|
282
|
+
``statistic_fn(series)``.
|
|
283
|
+
surrogate_values : ndarray of float, shape (n_surrogates,)
|
|
284
|
+
``statistic_fn`` evaluated on each surrogate.
|
|
285
|
+
significance : float
|
|
286
|
+
``|observed - mean(surrogate_values)| / std(surrogate_values)``: the
|
|
287
|
+
number of surrogate standard deviations the observed value is from
|
|
288
|
+
the surrogate mean. Values well above ~2-3 are typically taken as
|
|
289
|
+
evidence of nonlinear structure not explained by a linear process
|
|
290
|
+
with the same spectrum and distribution.
|
|
291
|
+
"""
|
|
292
|
+
series = np.asarray(series, dtype=np.float64)
|
|
293
|
+
rng = np.random.default_rng(seed)
|
|
294
|
+
observed = statistic_fn(series)
|
|
295
|
+
surrogate_values = np.array(
|
|
296
|
+
[
|
|
297
|
+
statistic_fn(iaaft_surrogate(series, n_iter=n_iter, seed=int(rng.integers(0, 2**32))))
|
|
298
|
+
for _ in tqdm(range(n_surrogates), disable=not show_progress, desc="surrogates")
|
|
299
|
+
]
|
|
300
|
+
)
|
|
301
|
+
sigma = float(np.std(surrogate_values))
|
|
302
|
+
significance = abs(observed - float(np.mean(surrogate_values))) / sigma if sigma > 0 else np.inf
|
|
303
|
+
return observed, surrogate_values, significance
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from physicskit.chaos.visualizers import theme
|
|
2
|
+
from physicskit.chaos.visualizers.basins import basin_of_attraction, plot_basin_of_attraction
|
|
3
|
+
from physicskit.chaos.visualizers.bifurcation import (
|
|
4
|
+
bifurcation_diagram,
|
|
5
|
+
map_bifurcation_sampler,
|
|
6
|
+
plot_bifurcation_diagram,
|
|
7
|
+
stroboscopic_bifurcation_sampler,
|
|
8
|
+
)
|
|
9
|
+
from physicskit.chaos.visualizers.divergence import (
|
|
10
|
+
billiard_trajectory_divergence,
|
|
11
|
+
plot_billiard_divergence,
|
|
12
|
+
plot_lyapunov_divergence,
|
|
13
|
+
plot_trajectory_ensemble,
|
|
14
|
+
trajectory_divergence,
|
|
15
|
+
trajectory_ensemble,
|
|
16
|
+
)
|
|
17
|
+
from physicskit.chaos.visualizers.dynamic_plots import (
|
|
18
|
+
animate_bakers_map,
|
|
19
|
+
animate_billiard_divergence,
|
|
20
|
+
animate_billiard_trajectory,
|
|
21
|
+
animate_double_pendulum,
|
|
22
|
+
animate_magnetic_pendulum,
|
|
23
|
+
animate_map_cobweb,
|
|
24
|
+
animate_map_orbit,
|
|
25
|
+
animate_multi_orbit_map,
|
|
26
|
+
animate_phase_volume_contraction,
|
|
27
|
+
animate_trajectory_2d,
|
|
28
|
+
animate_trajectory_3d,
|
|
29
|
+
plot_colored_trajectory,
|
|
30
|
+
plotly_3d_trajectory,
|
|
31
|
+
plotly_billiard_trajectory,
|
|
32
|
+
plotly_poincare_section,
|
|
33
|
+
)
|
|
34
|
+
from physicskit.chaos.visualizers.phase_space import plot_billiard_trajectory, plot_poincare_section
|
|
35
|
+
from physicskit.chaos.visualizers.quantum import (
|
|
36
|
+
animate_husimi_evolution,
|
|
37
|
+
plot_billiard_eigenstate,
|
|
38
|
+
plot_husimi,
|
|
39
|
+
plot_quantum_spectrum,
|
|
40
|
+
plot_weyl_law,
|
|
41
|
+
)
|
|
42
|
+
from physicskit.chaos.visualizers.recurrence import plot_recurrence_matrix, plot_recurrence_times
|
|
43
|
+
from physicskit.chaos.visualizers.section import plot_poincare_map, poincare_crossings
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"animate_bakers_map",
|
|
47
|
+
"animate_billiard_divergence",
|
|
48
|
+
"animate_billiard_trajectory",
|
|
49
|
+
"animate_double_pendulum",
|
|
50
|
+
"animate_husimi_evolution",
|
|
51
|
+
"animate_magnetic_pendulum",
|
|
52
|
+
"animate_map_cobweb",
|
|
53
|
+
"animate_map_orbit",
|
|
54
|
+
"animate_multi_orbit_map",
|
|
55
|
+
"animate_phase_volume_contraction",
|
|
56
|
+
"animate_trajectory_2d",
|
|
57
|
+
"animate_trajectory_3d",
|
|
58
|
+
"basin_of_attraction",
|
|
59
|
+
"bifurcation_diagram",
|
|
60
|
+
"billiard_trajectory_divergence",
|
|
61
|
+
"map_bifurcation_sampler",
|
|
62
|
+
"plot_basin_of_attraction",
|
|
63
|
+
"plot_bifurcation_diagram",
|
|
64
|
+
"plot_billiard_divergence",
|
|
65
|
+
"plot_billiard_eigenstate",
|
|
66
|
+
"plot_billiard_trajectory",
|
|
67
|
+
"plot_colored_trajectory",
|
|
68
|
+
"plot_husimi",
|
|
69
|
+
"plot_lyapunov_divergence",
|
|
70
|
+
"plot_poincare_map",
|
|
71
|
+
"plot_poincare_section",
|
|
72
|
+
"plot_quantum_spectrum",
|
|
73
|
+
"plot_recurrence_matrix",
|
|
74
|
+
"plot_recurrence_times",
|
|
75
|
+
"plot_trajectory_ensemble",
|
|
76
|
+
"plot_weyl_law",
|
|
77
|
+
"plotly_3d_trajectory",
|
|
78
|
+
"plotly_billiard_trajectory",
|
|
79
|
+
"plotly_poincare_section",
|
|
80
|
+
"poincare_crossings",
|
|
81
|
+
"stroboscopic_bifurcation_sampler",
|
|
82
|
+
"theme",
|
|
83
|
+
"trajectory_divergence",
|
|
84
|
+
"trajectory_ensemble",
|
|
85
|
+
]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Basin-of-attraction mapping: which stable state does each initial condition end up in?
|
|
2
|
+
|
|
3
|
+
For a multistable, dissipative system with several stable attractors (e.g.
|
|
4
|
+
:class:`physicskit.chaos.systems.continuous.MagneticPendulum`, with one attractor per
|
|
5
|
+
magnet), the basin of attraction of an attractor is the set of initial
|
|
6
|
+
conditions that end up there. Basin boundaries between attractors are often
|
|
7
|
+
*fractal* -- an arbitrarily small change in initial position can flip which
|
|
8
|
+
attractor "wins" -- and this module maps that structure over a 2D grid.
|
|
9
|
+
|
|
10
|
+
Because this means integrating one trajectory per pixel of the map, the
|
|
11
|
+
integration itself is a Numba-jitted, parallelized loop rather than the
|
|
12
|
+
:func:`scipy.integrate.solve_ivp`-based approach used by
|
|
13
|
+
:mod:`physicskit.chaos.visualizers.bifurcation`: `rhs` here must be a module-level
|
|
14
|
+
``@njit`` function (state, t, params) -> ndarray``, exactly like the ones
|
|
15
|
+
:mod:`physicskit.chaos.systems.continuous` exposes publicly (e.g.
|
|
16
|
+
:func:`physicskit.chaos.systems.continuous.magnetic_pendulum_rhs`).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from collections.abc import Callable
|
|
22
|
+
from typing import cast
|
|
23
|
+
|
|
24
|
+
import matplotlib.pyplot as plt
|
|
25
|
+
import numpy as np
|
|
26
|
+
from matplotlib.axes import Axes
|
|
27
|
+
from matplotlib.figure import Figure
|
|
28
|
+
from numba import njit, prange
|
|
29
|
+
from numpy.typing import ArrayLike, NDArray
|
|
30
|
+
|
|
31
|
+
from physicskit.chaos.core.integrators import rk4_step
|
|
32
|
+
from physicskit.chaos.visualizers import theme
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@njit(cache=True, parallel=True)
|
|
36
|
+
def _compute_basin_grid(rhs, xs, ys, vx0, vy0, dt, n_steps, attractors, params) -> NDArray[np.int64]:
|
|
37
|
+
n_y = ys.shape[0]
|
|
38
|
+
n_x = xs.shape[0]
|
|
39
|
+
n_attractors = attractors.shape[0]
|
|
40
|
+
labels = np.empty((n_y, n_x), dtype=np.int64)
|
|
41
|
+
for i in prange(n_y): # type: ignore[attr-defined] # numba lacks type stubs for prange
|
|
42
|
+
for j in range(n_x):
|
|
43
|
+
state = np.array([xs[j], ys[i], vx0, vy0])
|
|
44
|
+
for _ in range(n_steps):
|
|
45
|
+
state = rk4_step(rhs, state, 0.0, dt, params)
|
|
46
|
+
best_label = 0
|
|
47
|
+
best_dist = 1e18
|
|
48
|
+
for k in range(n_attractors):
|
|
49
|
+
dx = state[0] - attractors[k, 0]
|
|
50
|
+
dy = state[1] - attractors[k, 1]
|
|
51
|
+
dist = dx * dx + dy * dy
|
|
52
|
+
if dist < best_dist:
|
|
53
|
+
best_dist = dist
|
|
54
|
+
best_label = k
|
|
55
|
+
labels[i, j] = best_label
|
|
56
|
+
return labels
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def basin_of_attraction(
|
|
60
|
+
rhs: Callable,
|
|
61
|
+
params: ArrayLike,
|
|
62
|
+
x_range: tuple[float, float],
|
|
63
|
+
y_range: tuple[float, float],
|
|
64
|
+
attractors: ArrayLike,
|
|
65
|
+
resolution: int = 200,
|
|
66
|
+
velocity0: tuple[float, float] = (0.0, 0.0),
|
|
67
|
+
dt: float = 0.02,
|
|
68
|
+
n_steps: int = 2000,
|
|
69
|
+
) -> tuple[NDArray[np.float64], NDArray[np.float64], NDArray[np.int64]]:
|
|
70
|
+
"""Compute a basin-of-attraction map over a 2D grid of initial positions.
|
|
71
|
+
|
|
72
|
+
For every point on an ``resolution x resolution`` grid spanning
|
|
73
|
+
`x_range` x `y_range`, integrates a trajectory of the system defined by
|
|
74
|
+
`rhs` starting at that position (with initial velocity `velocity0`) for
|
|
75
|
+
`n_steps` of size `dt`, then labels the grid point by whichever entry of
|
|
76
|
+
`attractors` its final position ends up closest to.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
rhs : callable
|
|
81
|
+
A module-level Numba ``@njit`` function ``rhs(state, t, params) ->
|
|
82
|
+
ndarray``, with a 4-dimensional state ``(x, y, vx, vy)`` (e.g.
|
|
83
|
+
:func:`physicskit.chaos.systems.continuous.magnetic_pendulum_rhs`).
|
|
84
|
+
params : array_like of float
|
|
85
|
+
Parameter vector passed through to `rhs`.
|
|
86
|
+
x_range, y_range : tuple of float
|
|
87
|
+
``(min, max)`` extent of the grid along each axis.
|
|
88
|
+
attractors : array_like of float, shape (n_attractors, 2)
|
|
89
|
+
Known ``(x, y)`` positions of the system's stable attractors.
|
|
90
|
+
resolution : int, default 200
|
|
91
|
+
Number of grid points along each axis (the grid has
|
|
92
|
+
``resolution**2`` points total, each requiring one full
|
|
93
|
+
trajectory integration).
|
|
94
|
+
velocity0 : tuple of float, default (0.0, 0.0)
|
|
95
|
+
Initial velocity ``(vx, vy)``, the same for every grid point.
|
|
96
|
+
dt : float, default 0.02
|
|
97
|
+
Integration step size.
|
|
98
|
+
n_steps : int, default 2000
|
|
99
|
+
Number of integration steps per grid point; should be long enough
|
|
100
|
+
for the trajectory to have settled near an attractor.
|
|
101
|
+
|
|
102
|
+
Returns
|
|
103
|
+
-------
|
|
104
|
+
xs, ys : ndarray of float, shape (resolution,)
|
|
105
|
+
Grid coordinates along each axis.
|
|
106
|
+
labels : ndarray of int64, shape (resolution, resolution)
|
|
107
|
+
``labels[i, j]`` is the index into `attractors` of the attractor
|
|
108
|
+
closest to the trajectory launched from ``(xs[j], ys[i])``.
|
|
109
|
+
"""
|
|
110
|
+
xs = np.linspace(x_range[0], x_range[1], resolution)
|
|
111
|
+
ys = np.linspace(y_range[0], y_range[1], resolution)
|
|
112
|
+
labels = _compute_basin_grid(
|
|
113
|
+
rhs,
|
|
114
|
+
xs,
|
|
115
|
+
ys,
|
|
116
|
+
float(velocity0[0]),
|
|
117
|
+
float(velocity0[1]),
|
|
118
|
+
dt,
|
|
119
|
+
n_steps,
|
|
120
|
+
np.asarray(attractors, dtype=np.float64),
|
|
121
|
+
np.asarray(params, dtype=np.float64),
|
|
122
|
+
)
|
|
123
|
+
return xs, ys, labels
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def plot_basin_of_attraction(
|
|
127
|
+
rhs: Callable,
|
|
128
|
+
params: ArrayLike,
|
|
129
|
+
x_range: tuple[float, float],
|
|
130
|
+
y_range: tuple[float, float],
|
|
131
|
+
attractors: ArrayLike,
|
|
132
|
+
resolution: int = 200,
|
|
133
|
+
velocity0: tuple[float, float] = (0.0, 0.0),
|
|
134
|
+
dt: float = 0.02,
|
|
135
|
+
n_steps: int = 2000,
|
|
136
|
+
ax: Axes | None = None,
|
|
137
|
+
cmap: str = theme.QUALITATIVE_CMAP,
|
|
138
|
+
) -> tuple[Figure, Axes]:
|
|
139
|
+
"""Plot a basin-of-attraction map.
|
|
140
|
+
|
|
141
|
+
Parameters
|
|
142
|
+
----------
|
|
143
|
+
rhs, params, x_range, y_range, attractors, resolution, velocity0, dt, n_steps
|
|
144
|
+
See :func:`basin_of_attraction`.
|
|
145
|
+
ax : matplotlib.axes.Axes, optional
|
|
146
|
+
Axes to draw into; a new figure and axes are created if omitted.
|
|
147
|
+
cmap : str, default "tab10" (:data:`physicskit.chaos.visualizers.theme.QUALITATIVE_CMAP`)
|
|
148
|
+
Colormap used for the (discrete) attractor labels.
|
|
149
|
+
|
|
150
|
+
Returns
|
|
151
|
+
-------
|
|
152
|
+
fig : matplotlib.figure.Figure
|
|
153
|
+
ax : matplotlib.axes.Axes
|
|
154
|
+
"""
|
|
155
|
+
xs, ys, labels = basin_of_attraction(
|
|
156
|
+
rhs,
|
|
157
|
+
params,
|
|
158
|
+
x_range,
|
|
159
|
+
y_range,
|
|
160
|
+
attractors,
|
|
161
|
+
resolution=resolution,
|
|
162
|
+
velocity0=velocity0,
|
|
163
|
+
dt=dt,
|
|
164
|
+
n_steps=n_steps,
|
|
165
|
+
)
|
|
166
|
+
attractors = np.asarray(attractors, dtype=np.float64)
|
|
167
|
+
|
|
168
|
+
if ax is None:
|
|
169
|
+
fig, ax = plt.subplots(figsize=(7, 7))
|
|
170
|
+
else:
|
|
171
|
+
fig = cast(Figure, ax.figure)
|
|
172
|
+
|
|
173
|
+
ax.imshow(
|
|
174
|
+
labels,
|
|
175
|
+
extent=(xs.min(), xs.max(), ys.min(), ys.max()),
|
|
176
|
+
origin="lower",
|
|
177
|
+
cmap=cmap,
|
|
178
|
+
interpolation="nearest",
|
|
179
|
+
aspect="equal",
|
|
180
|
+
)
|
|
181
|
+
ax.scatter(attractors[:, 0], attractors[:, 1], c=theme.STRUCTURE, marker="x", s=60, zorder=3)
|
|
182
|
+
ax.set_xlabel("x")
|
|
183
|
+
ax.set_ylabel("y")
|
|
184
|
+
ax.set_title("Basin of attraction")
|
|
185
|
+
return fig, ax
|