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,304 @@
|
|
|
1
|
+
"""Metrics for quantifying chaos: Lyapunov exponents, energy drift, phase-volume expansion."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
|
+
|
|
10
|
+
from physicskit.chaos.core.base_system import DiscreteMap, DynamicalSystem
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def lyapunov_exponent_from_divergence(t: NDArray[np.float64], delta: NDArray[np.float64]) -> float:
|
|
14
|
+
"""Estimate the largest Lyapunov exponent from a trajectory-separation curve.
|
|
15
|
+
|
|
16
|
+
The estimate is the slope of ``ln(delta)`` vs. `t`, fit by ordinary least
|
|
17
|
+
squares.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
t : ndarray of float, shape (n,)
|
|
22
|
+
Time points.
|
|
23
|
+
delta : ndarray of float, shape (n,)
|
|
24
|
+
The (positive) separation between two initially nearby trajectories
|
|
25
|
+
at each time in `t`, e.g. as produced by
|
|
26
|
+
:func:`physicskit.chaos.visualizers.divergence.trajectory_divergence`.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
float
|
|
31
|
+
Estimated largest Lyapunov exponent ``lambda_max``.
|
|
32
|
+
"""
|
|
33
|
+
t = np.asarray(t, dtype=np.float64)
|
|
34
|
+
delta = np.asarray(delta, dtype=np.float64)
|
|
35
|
+
mask = delta > 0
|
|
36
|
+
slope, _ = np.polyfit(t[mask], np.log(delta[mask]), 1)
|
|
37
|
+
return float(slope)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def numerical_jacobian(system: DynamicalSystem, state: NDArray[np.float64], t: float, eps: float = 1e-6) -> NDArray[np.float64]:
|
|
41
|
+
"""Central-difference Jacobian of a dynamical system's vector field.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
system : DynamicalSystem
|
|
46
|
+
System whose ``rhs(state, t)`` is to be differentiated.
|
|
47
|
+
state : ndarray of float, shape (dim,)
|
|
48
|
+
State at which to evaluate the Jacobian.
|
|
49
|
+
t : float
|
|
50
|
+
Time at which to evaluate the Jacobian.
|
|
51
|
+
eps : float, default 1e-6
|
|
52
|
+
Finite-difference step size.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
ndarray of float, shape (dim, dim)
|
|
57
|
+
Jacobian matrix ``d(rhs)/d(state)`` at ``(state, t)``.
|
|
58
|
+
"""
|
|
59
|
+
state = np.asarray(state, dtype=np.float64)
|
|
60
|
+
dim = state.shape[0]
|
|
61
|
+
jac = np.empty((dim, dim))
|
|
62
|
+
for j in range(dim):
|
|
63
|
+
perturb = np.zeros(dim)
|
|
64
|
+
perturb[j] = eps
|
|
65
|
+
f_plus = system.rhs(state + perturb, t)
|
|
66
|
+
f_minus = system.rhs(state - perturb, t)
|
|
67
|
+
jac[:, j] = (f_plus - f_minus) / (2.0 * eps)
|
|
68
|
+
return jac
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def benettin_lyapunov_spectrum(
|
|
72
|
+
system: DynamicalSystem,
|
|
73
|
+
state0: NDArray[np.float64],
|
|
74
|
+
dt: float = 0.01,
|
|
75
|
+
n_steps: int = 5000,
|
|
76
|
+
n_transient: int = 500,
|
|
77
|
+
) -> NDArray[np.float64]:
|
|
78
|
+
"""Estimate the full Lyapunov spectrum via the Benettin QR method.
|
|
79
|
+
|
|
80
|
+
Integrates the reference trajectory and an orthonormal tangent-space
|
|
81
|
+
frame *simultaneously* with matching-order RK4 (the frame's variational
|
|
82
|
+
equation ``dY/dt = J(x(t)) Y`` is evaluated, via the numerical Jacobian,
|
|
83
|
+
at each of RK4's four substages, exactly as the state itself is),
|
|
84
|
+
periodically re-orthonormalizing the frame with QR decomposition and
|
|
85
|
+
accumulating the log-growth of each axis. Integrating the frame at only
|
|
86
|
+
1st-order (e.g. forward Euler) while the reference trajectory uses RK4
|
|
87
|
+
would bias the smallest-magnitude exponents (whose true value is often
|
|
88
|
+
near zero) by an :math:`O(\\mathrm{dt})` error that does not shrink
|
|
89
|
+
merely by integrating for longer -- only by refining `dt` itself.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
system : DynamicalSystem
|
|
94
|
+
System whose Lyapunov spectrum is to be estimated.
|
|
95
|
+
state0 : array_like of float, shape (dim,)
|
|
96
|
+
Initial state of the reference trajectory.
|
|
97
|
+
dt : float, default 0.01
|
|
98
|
+
Integration step size.
|
|
99
|
+
n_steps : int, default 5000
|
|
100
|
+
Total number of integration steps.
|
|
101
|
+
n_transient : int, default 500
|
|
102
|
+
Number of initial steps discarded before accumulating exponents, to
|
|
103
|
+
let the frame align with the attractor.
|
|
104
|
+
|
|
105
|
+
Returns
|
|
106
|
+
-------
|
|
107
|
+
ndarray of float, shape (dim,)
|
|
108
|
+
Estimated Lyapunov exponents, in descending order.
|
|
109
|
+
"""
|
|
110
|
+
state = np.asarray(state0, dtype=np.float64).copy()
|
|
111
|
+
dim = state.shape[0]
|
|
112
|
+
frame = np.eye(dim)
|
|
113
|
+
log_sums = np.zeros(dim)
|
|
114
|
+
t = 0.0
|
|
115
|
+
n_accum = 0
|
|
116
|
+
|
|
117
|
+
for i in range(n_steps):
|
|
118
|
+
k1_x = system.rhs(state, t)
|
|
119
|
+
k1_f = numerical_jacobian(system, state, t) @ frame
|
|
120
|
+
|
|
121
|
+
state_2 = state + 0.5 * dt * k1_x
|
|
122
|
+
t_2 = t + 0.5 * dt
|
|
123
|
+
k2_x = system.rhs(state_2, t_2)
|
|
124
|
+
k2_f = numerical_jacobian(system, state_2, t_2) @ (frame + 0.5 * dt * k1_f)
|
|
125
|
+
|
|
126
|
+
state_3 = state + 0.5 * dt * k2_x
|
|
127
|
+
k3_x = system.rhs(state_3, t_2)
|
|
128
|
+
k3_f = numerical_jacobian(system, state_3, t_2) @ (frame + 0.5 * dt * k2_f)
|
|
129
|
+
|
|
130
|
+
state_4 = state + dt * k3_x
|
|
131
|
+
t_4 = t + dt
|
|
132
|
+
k4_x = system.rhs(state_4, t_4)
|
|
133
|
+
k4_f = numerical_jacobian(system, state_4, t_4) @ (frame + dt * k3_f)
|
|
134
|
+
|
|
135
|
+
state_next = state + (dt / 6.0) * (k1_x + 2 * k2_x + 2 * k3_x + k4_x)
|
|
136
|
+
frame_next = frame + (dt / 6.0) * (k1_f + 2 * k2_f + 2 * k3_f + k4_f)
|
|
137
|
+
|
|
138
|
+
q, r = np.linalg.qr(frame_next)
|
|
139
|
+
signs = np.sign(np.diag(r))
|
|
140
|
+
signs[signs == 0] = 1.0
|
|
141
|
+
q = q * signs
|
|
142
|
+
r = (r.T * signs).T
|
|
143
|
+
frame = q
|
|
144
|
+
|
|
145
|
+
if i >= n_transient:
|
|
146
|
+
log_sums += np.log(np.abs(np.diag(r)))
|
|
147
|
+
n_accum += 1
|
|
148
|
+
|
|
149
|
+
state = state_next
|
|
150
|
+
t += dt
|
|
151
|
+
|
|
152
|
+
elapsed = n_accum * dt
|
|
153
|
+
return log_sums / elapsed
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def numerical_jacobian_map(dmap: DiscreteMap, state: NDArray[np.float64], eps: float = 1e-6) -> NDArray[np.float64]:
|
|
157
|
+
"""Central-difference Jacobian of a discrete map's step function.
|
|
158
|
+
|
|
159
|
+
Parameters
|
|
160
|
+
----------
|
|
161
|
+
dmap : DiscreteMap
|
|
162
|
+
Map whose ``step(state)`` is to be differentiated.
|
|
163
|
+
state : ndarray of float, shape (dim,)
|
|
164
|
+
State at which to evaluate the Jacobian.
|
|
165
|
+
eps : float, default 1e-6
|
|
166
|
+
Finite-difference step size.
|
|
167
|
+
|
|
168
|
+
Returns
|
|
169
|
+
-------
|
|
170
|
+
ndarray of float, shape (dim, dim)
|
|
171
|
+
Jacobian matrix ``d(step)/d(state)`` at `state`.
|
|
172
|
+
"""
|
|
173
|
+
state = np.asarray(state, dtype=np.float64)
|
|
174
|
+
dim = state.shape[0]
|
|
175
|
+
jac = np.empty((dim, dim))
|
|
176
|
+
for j in range(dim):
|
|
177
|
+
perturb = np.zeros(dim)
|
|
178
|
+
perturb[j] = eps
|
|
179
|
+
f_plus = dmap.step(state + perturb)
|
|
180
|
+
f_minus = dmap.step(state - perturb)
|
|
181
|
+
jac[:, j] = (f_plus - f_minus) / (2.0 * eps)
|
|
182
|
+
return jac
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def map_lyapunov_spectrum(
|
|
186
|
+
dmap: DiscreteMap,
|
|
187
|
+
state0: NDArray[np.float64],
|
|
188
|
+
n_iter: int = 5000,
|
|
189
|
+
n_transient: int = 500,
|
|
190
|
+
) -> NDArray[np.float64]:
|
|
191
|
+
"""Estimate the full Lyapunov spectrum of a discrete map via the Benettin QR method.
|
|
192
|
+
|
|
193
|
+
The discrete-map analog of :func:`benettin_lyapunov_spectrum`: co-evolves
|
|
194
|
+
an orthonormal frame under the linearized (numerically-Jacobian'd) map
|
|
195
|
+
alongside the reference orbit, periodically re-orthonormalizing with QR
|
|
196
|
+
decomposition and accumulating the log-growth of each axis. Unlike the
|
|
197
|
+
continuous-flow version, there is no `dt` to divide by -- exponents come
|
|
198
|
+
out directly in units of "per iteration".
|
|
199
|
+
|
|
200
|
+
Parameters
|
|
201
|
+
----------
|
|
202
|
+
dmap : DiscreteMap
|
|
203
|
+
Map whose Lyapunov spectrum is to be estimated.
|
|
204
|
+
state0 : array_like of float, shape (dim,)
|
|
205
|
+
Initial state of the reference orbit.
|
|
206
|
+
n_iter : int, default 5000
|
|
207
|
+
Total number of map iterations.
|
|
208
|
+
n_transient : int, default 500
|
|
209
|
+
Number of initial iterations discarded before accumulating
|
|
210
|
+
exponents, to let the frame align with the attractor.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
ndarray of float, shape (dim,)
|
|
215
|
+
Estimated Lyapunov exponents, in descending order, in units of "per
|
|
216
|
+
iteration".
|
|
217
|
+
"""
|
|
218
|
+
state = np.asarray(state0, dtype=np.float64).copy()
|
|
219
|
+
dim = state.shape[0]
|
|
220
|
+
frame = np.eye(dim)
|
|
221
|
+
log_sums = np.zeros(dim)
|
|
222
|
+
n_accum = 0
|
|
223
|
+
|
|
224
|
+
for i in range(n_iter):
|
|
225
|
+
jac = numerical_jacobian_map(dmap, state)
|
|
226
|
+
frame = jac @ frame
|
|
227
|
+
state = dmap.step(state)
|
|
228
|
+
|
|
229
|
+
q, r = np.linalg.qr(frame)
|
|
230
|
+
signs = np.sign(np.diag(r))
|
|
231
|
+
signs[signs == 0] = 1.0
|
|
232
|
+
q = q * signs
|
|
233
|
+
r = (r.T * signs).T
|
|
234
|
+
frame = q
|
|
235
|
+
|
|
236
|
+
if i >= n_transient:
|
|
237
|
+
log_sums += np.log(np.abs(np.diag(r)))
|
|
238
|
+
n_accum += 1
|
|
239
|
+
|
|
240
|
+
return log_sums / n_accum
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def energy_drift(
|
|
244
|
+
times: NDArray[np.float64],
|
|
245
|
+
states: NDArray[np.float64],
|
|
246
|
+
energy_func: Callable[[NDArray[np.float64]], float],
|
|
247
|
+
) -> NDArray[np.float64]:
|
|
248
|
+
"""Relative energy drift along a trajectory.
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
times : ndarray of float, shape (n,)
|
|
253
|
+
Time points (unused directly, kept for API symmetry with
|
|
254
|
+
:func:`phase_volume_expansion`).
|
|
255
|
+
states : ndarray of float, shape (n, dim)
|
|
256
|
+
State at each time in `times`.
|
|
257
|
+
energy_func : callable
|
|
258
|
+
Function ``energy_func(state) -> float`` computing the conserved (or
|
|
259
|
+
nominally conserved) quantity for a single state; useful for checking
|
|
260
|
+
integrator fidelity (e.g.
|
|
261
|
+
:meth:`physicskit.chaos.systems.continuous.DoublePendulum.energy`).
|
|
262
|
+
|
|
263
|
+
Returns
|
|
264
|
+
-------
|
|
265
|
+
ndarray of float, shape (n,)
|
|
266
|
+
Relative drift ``(E(t) - E(0)) / |E(0)|`` at each time in `times`
|
|
267
|
+
(using an absolute, not relative, denominator when ``E(0)`` is
|
|
268
|
+
numerically zero).
|
|
269
|
+
"""
|
|
270
|
+
energies = np.array([energy_func(states[i]) for i in range(states.shape[0])])
|
|
271
|
+
e0 = energies[0]
|
|
272
|
+
denom = abs(e0) if abs(e0) > 1e-12 else 1.0
|
|
273
|
+
return np.asarray((energies - e0) / denom, dtype=np.float64)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def phase_volume_expansion(system: DynamicalSystem, times: NDArray[np.float64], states: NDArray[np.float64]) -> NDArray[np.float64]:
|
|
277
|
+
"""Cumulative log phase-space-volume expansion along a trajectory.
|
|
278
|
+
|
|
279
|
+
Uses Liouville's theorem, ``d(log V)/dt = trace(Jacobian)``, integrated
|
|
280
|
+
with the trapezoidal rule.
|
|
281
|
+
|
|
282
|
+
Parameters
|
|
283
|
+
----------
|
|
284
|
+
system : DynamicalSystem
|
|
285
|
+
System whose phase-space divergence (``trace`` of the Jacobian) is
|
|
286
|
+
evaluated along the trajectory.
|
|
287
|
+
times : ndarray of float, shape (n,)
|
|
288
|
+
Time points.
|
|
289
|
+
states : ndarray of float, shape (n, dim)
|
|
290
|
+
State at each time in `times`.
|
|
291
|
+
|
|
292
|
+
Returns
|
|
293
|
+
-------
|
|
294
|
+
ndarray of float, shape (n,)
|
|
295
|
+
Cumulative ``log(V(t) / V(0))`` at each time in `times`, starting at
|
|
296
|
+
``0.0``. Negative values indicate a dissipative (volume-contracting)
|
|
297
|
+
system.
|
|
298
|
+
"""
|
|
299
|
+
n = states.shape[0]
|
|
300
|
+
divergence = np.empty(n)
|
|
301
|
+
for i in range(n):
|
|
302
|
+
jac = numerical_jacobian(system, states[i], times[i])
|
|
303
|
+
divergence[i] = np.trace(jac)
|
|
304
|
+
return np.concatenate(([0.0], np.cumsum(0.5 * (divergence[:-1] + divergence[1:]) * np.diff(times))))
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Poincare recurrence analysis: return-time statistics and Kac's lemma.
|
|
2
|
+
|
|
3
|
+
The Poincare recurrence theorem guarantees that, for a measure-preserving
|
|
4
|
+
dynamical system, almost every trajectory returns arbitrarily close to its
|
|
5
|
+
starting point infinitely often. Kac's lemma sharpens this: for an ergodic
|
|
6
|
+
system, the *mean* time between successive returns to a region ``A`` equals
|
|
7
|
+
the reciprocal of that region's invariant measure, ``mean_recurrence_time ~=
|
|
8
|
+
1 / mu(A)``. The functions here compute both sides of that relationship
|
|
9
|
+
numerically from a sampled trajectory.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import numpy as np
|
|
15
|
+
from numpy.typing import ArrayLike, NDArray
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def recurrence_matrix(states: ArrayLike, epsilon: float) -> NDArray[np.bool_]:
|
|
19
|
+
"""The pairwise epsilon-neighborhood recurrence matrix of a trajectory.
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
states : array_like of float, shape (n, dim)
|
|
24
|
+
Trajectory samples.
|
|
25
|
+
epsilon : float
|
|
26
|
+
Neighborhood radius.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
ndarray of bool, shape (n, n)
|
|
31
|
+
``R[i, j]`` is ``True`` iff ``states[i]`` and ``states[j]`` are within
|
|
32
|
+
`epsilon` of each other (Euclidean distance). The classic "recurrence
|
|
33
|
+
plot" of nonlinear time series analysis is this matrix rendered as an
|
|
34
|
+
image; see :func:`physicskit.chaos.visualizers.recurrence.plot_recurrence_matrix`.
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
This computes all :math:`O(n^2)` pairwise distances; keep `states` to at
|
|
39
|
+
most a few thousand rows.
|
|
40
|
+
"""
|
|
41
|
+
states = np.asarray(states, dtype=np.float64)
|
|
42
|
+
diffs = states[:, np.newaxis, :] - states[np.newaxis, :, :]
|
|
43
|
+
dists = np.sqrt(np.sum(diffs * diffs, axis=-1))
|
|
44
|
+
return dists < epsilon
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def recurrence_times(states: ArrayLike, epsilon: float, reference_idx: int = 0, dt: float = 1.0) -> NDArray[np.float64]:
|
|
48
|
+
"""Gap times between successive returns of a trajectory to a reference point.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
states : array_like of float, shape (n, dim)
|
|
53
|
+
Trajectory samples.
|
|
54
|
+
epsilon : float
|
|
55
|
+
Neighborhood radius defining a "return".
|
|
56
|
+
reference_idx : int, default 0
|
|
57
|
+
Index into `states` of the reference point to measure returns to.
|
|
58
|
+
dt : float, default 1.0
|
|
59
|
+
Time between consecutive rows of `states`, used to convert the
|
|
60
|
+
(integer) step counts between returns into physical time units.
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
ndarray of float, shape (m,)
|
|
65
|
+
The `m` gap times between successive entries into the
|
|
66
|
+
epsilon-neighborhood of ``states[reference_idx]`` (``m`` is the
|
|
67
|
+
number of *returns* observed minus one; empty if there were fewer
|
|
68
|
+
than two returns).
|
|
69
|
+
"""
|
|
70
|
+
states = np.asarray(states, dtype=np.float64)
|
|
71
|
+
reference = states[reference_idx]
|
|
72
|
+
dists = np.sqrt(np.sum((states - reference) ** 2, axis=-1))
|
|
73
|
+
inside = dists < epsilon
|
|
74
|
+
# "Entries" are steps where the trajectory just crossed from outside the
|
|
75
|
+
# ball to inside it -- each one is a single recurrence event, even if the
|
|
76
|
+
# trajectory then lingers nearby for several consecutive samples.
|
|
77
|
+
entries = np.flatnonzero(inside[1:] & ~inside[:-1]) + 1
|
|
78
|
+
return np.diff(entries).astype(np.float64) * dt
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def kac_lemma_estimate(states: ArrayLike, epsilon: float, reference_idx: int = 0, dt: float = 1.0) -> tuple[float, float, float]:
|
|
82
|
+
"""Numerically validate Kac's lemma on a sampled trajectory.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
states : array_like of float, shape (n, dim)
|
|
87
|
+
Trajectory samples, assumed to come from an ergodic,
|
|
88
|
+
measure-preserving (or already attractor-restricted, invariant-measure
|
|
89
|
+
sampling) system.
|
|
90
|
+
epsilon : float
|
|
91
|
+
Neighborhood radius defining "returns" and the region whose measure
|
|
92
|
+
is estimated.
|
|
93
|
+
reference_idx : int, default 0
|
|
94
|
+
Index into `states` of the reference point.
|
|
95
|
+
dt : float, default 1.0
|
|
96
|
+
Time between consecutive rows of `states`.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
mean_recurrence_time : float
|
|
101
|
+
The mean of :func:`recurrence_times` for this trajectory.
|
|
102
|
+
measure_estimate : float
|
|
103
|
+
The empirical invariant measure of the epsilon-neighborhood: the
|
|
104
|
+
fraction of all sampled points in `states` lying within `epsilon` of
|
|
105
|
+
``states[reference_idx]``.
|
|
106
|
+
kac_product : float
|
|
107
|
+
``mean_recurrence_time * measure_estimate / dt``, which Kac's lemma
|
|
108
|
+
predicts should be close to 1 for a long-enough, ergodic trajectory
|
|
109
|
+
(the ``/dt`` converts the measure, a dimensionless fraction, onto the
|
|
110
|
+
same per-step footing as a recurrence time measured in physical
|
|
111
|
+
time units).
|
|
112
|
+
"""
|
|
113
|
+
states = np.asarray(states, dtype=np.float64)
|
|
114
|
+
times = recurrence_times(states, epsilon, reference_idx=reference_idx, dt=dt)
|
|
115
|
+
mean_time = float(np.mean(times)) if times.size else float("nan")
|
|
116
|
+
|
|
117
|
+
reference = states[reference_idx]
|
|
118
|
+
dists = np.sqrt(np.sum((states - reference) ** 2, axis=-1))
|
|
119
|
+
measure = float(np.mean(dists < epsilon))
|
|
120
|
+
|
|
121
|
+
kac_product = mean_time * measure / dt
|
|
122
|
+
return mean_time, measure, kac_product
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Spectral diagnostics: power spectrum and autocorrelation.
|
|
2
|
+
|
|
3
|
+
The cheapest, most classical way to distinguish periodic, quasiperiodic, and
|
|
4
|
+
chaotic behavior at a glance: a periodic signal's power spectrum is a forest
|
|
5
|
+
of sharp lines, a quasiperiodic one a handful of incommensurate lines (plus
|
|
6
|
+
their sums/differences), and a chaotic one a broadband continuum. These
|
|
7
|
+
functions work on any scalar time series -- a physicskit.chaos-simulated trajectory
|
|
8
|
+
component or independently-supplied data alike.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
from numpy.typing import ArrayLike, NDArray
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def power_spectrum(series: ArrayLike, dt: float = 1.0) -> tuple[NDArray[np.float64], NDArray[np.float64]]:
|
|
18
|
+
"""Compute the power spectrum of a (real-valued) scalar time series.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
series : array_like of float, shape (n,)
|
|
23
|
+
Time series (its mean is removed before transforming, so the
|
|
24
|
+
spectrum reflects only its fluctuations).
|
|
25
|
+
dt : float, default 1.0
|
|
26
|
+
Time between consecutive samples.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
frequencies : ndarray of float, shape (n // 2 + 1,)
|
|
31
|
+
Non-negative frequencies, in cycles per unit time (i.e. matching
|
|
32
|
+
1/`dt` units).
|
|
33
|
+
power : ndarray of float, shape (n // 2 + 1,)
|
|
34
|
+
Power at each frequency (``|FFT|^2 / n``).
|
|
35
|
+
"""
|
|
36
|
+
series = np.asarray(series, dtype=np.float64)
|
|
37
|
+
series = series - series.mean()
|
|
38
|
+
n = series.shape[0]
|
|
39
|
+
spectrum = np.fft.rfft(series)
|
|
40
|
+
power = (np.abs(spectrum) ** 2) / n
|
|
41
|
+
frequencies = np.fft.rfftfreq(n, d=dt)
|
|
42
|
+
return frequencies, power
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def autocorrelation(series: ArrayLike, max_lag: int | None = None) -> NDArray[np.float64]:
|
|
46
|
+
"""Compute the (normalized) autocorrelation function of a scalar time series.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
series : array_like of float, shape (n,)
|
|
51
|
+
Time series (its mean is removed before correlating).
|
|
52
|
+
max_lag : int, optional
|
|
53
|
+
Largest lag to compute, in samples; defaults to ``n - 1``.
|
|
54
|
+
|
|
55
|
+
Returns
|
|
56
|
+
-------
|
|
57
|
+
ndarray of float, shape (max_lag + 1,)
|
|
58
|
+
The autocorrelation at lags ``0, 1, ..., max_lag``, normalized so
|
|
59
|
+
that ``result[0] == 1.0``.
|
|
60
|
+
"""
|
|
61
|
+
series = np.asarray(series, dtype=np.float64)
|
|
62
|
+
series = series - series.mean()
|
|
63
|
+
n = series.shape[0]
|
|
64
|
+
if max_lag is None:
|
|
65
|
+
max_lag = n - 1
|
|
66
|
+
|
|
67
|
+
full = np.correlate(series, series, mode="full")
|
|
68
|
+
zero_lag = full[full.shape[0] // 2 :]
|
|
69
|
+
return np.asarray(zero_lag[: max_lag + 1] / zero_lag[0], dtype=np.float64)
|