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,73 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.chaos.systems.billiards import BunimovichStadium, CircleBilliard, SinaiBilliard
|
|
5
|
+
from physicskit.chaos.visualizers.phase_space import plot_billiard_trajectory, plot_poincare_section
|
|
6
|
+
|
|
7
|
+
ALL_BILLIARDS = [
|
|
8
|
+
lambda: CircleBilliard(radius=1.0),
|
|
9
|
+
lambda: SinaiBilliard(cell_size=2.0, scatterer_radius=0.5),
|
|
10
|
+
lambda: BunimovichStadium(radius=1.0, straight_length=2.0),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize("make_billiard", ALL_BILLIARDS)
|
|
15
|
+
def test_simulate_many_rays_matches_per_ray_simulate(make_billiard):
|
|
16
|
+
"""The parallel batch kernel must reproduce plain per-ray simulate() exactly."""
|
|
17
|
+
billiard = make_billiard()
|
|
18
|
+
pos = billiard.sample_interior_point()
|
|
19
|
+
angles = np.array([0.3, 1.1, 2.7, 4.0, 5.9])
|
|
20
|
+
|
|
21
|
+
batch = billiard.simulate_many_rays(pos, angles, n_bounces=30)
|
|
22
|
+
|
|
23
|
+
expected_s, expected_sin_phi = [], []
|
|
24
|
+
for angle in angles:
|
|
25
|
+
vel = np.array([np.cos(angle), np.sin(angle)])
|
|
26
|
+
result = billiard.simulate(pos, vel, n_bounces=30)
|
|
27
|
+
expected_s.append(result["s"])
|
|
28
|
+
expected_sin_phi.append(result["sin_phi"])
|
|
29
|
+
|
|
30
|
+
np.testing.assert_allclose(batch["s"], np.concatenate(expected_s))
|
|
31
|
+
np.testing.assert_allclose(batch["sin_phi"], np.concatenate(expected_sin_phi))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_plot_poincare_section_returns_pooled_scatter_within_bounds():
|
|
35
|
+
billiard = SinaiBilliard(cell_size=2.0, scatterer_radius=0.5)
|
|
36
|
+
_, ax = plot_poincare_section(billiard, n_rays=8, n_bounces=20, seed=0)
|
|
37
|
+
collection = ax.collections[0]
|
|
38
|
+
offsets = collection.get_offsets()
|
|
39
|
+
assert offsets.shape == (8 * 20, 2)
|
|
40
|
+
s, sin_phi = offsets[:, 0], offsets[:, 1]
|
|
41
|
+
assert np.all(s >= 0.0) and np.all(s <= billiard.perimeter())
|
|
42
|
+
assert np.all(sin_phi >= -1.0) and np.all(sin_phi <= 1.0)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_plot_billiard_trajectory_draws_boundary_and_path():
|
|
46
|
+
billiard = CircleBilliard(radius=1.0)
|
|
47
|
+
pos = billiard.sample_interior_point()
|
|
48
|
+
_, ax = plot_billiard_trajectory(billiard, pos, vel=(1.0, 0.3), n_bounces=15)
|
|
49
|
+
assert len(ax.lines) == 2
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_plot_poincare_section_defaults_to_billiards_own_interior_point():
|
|
53
|
+
"""Rays launched from a circle's exact center (its default interior
|
|
54
|
+
point) all hit the boundary head-on, so every ray shares sin(phi) = 0 --
|
|
55
|
+
a documented degenerate case (see test below), not a bug in the default
|
|
56
|
+
itself: the default must still be ``sample_interior_point()``."""
|
|
57
|
+
billiard = CircleBilliard(radius=1.0)
|
|
58
|
+
_, ax = plot_poincare_section(billiard, n_rays=10, n_bounces=20, seed=0)
|
|
59
|
+
sin_phi = ax.collections[0].get_offsets()[:, 1]
|
|
60
|
+
assert np.allclose(sin_phi, 0.0, atol=1e-9)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_plot_poincare_section_pos_override_avoids_circle_center_degeneracy():
|
|
64
|
+
"""An off-center pos, unlike the default center, must produce a genuine
|
|
65
|
+
family of distinct invariant curves (one sin(phi) per ray, not all the
|
|
66
|
+
same value)."""
|
|
67
|
+
billiard = CircleBilliard(radius=1.0)
|
|
68
|
+
_, ax = plot_poincare_section(billiard, n_rays=10, n_bounces=20, pos=(0.3, 0.0), seed=0)
|
|
69
|
+
offsets = ax.collections[0].get_offsets()
|
|
70
|
+
sin_phi_per_ray = np.asarray(offsets[:, 1]).reshape(10, 20)
|
|
71
|
+
per_ray_values = sin_phi_per_ray[:, 0]
|
|
72
|
+
assert not np.allclose(sin_phi_per_ray, 0.0, atol=1e-9)
|
|
73
|
+
assert np.unique(np.round(per_ray_values, 6)).size == 10
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Property-based tests (Hypothesis) for invariants that should hold across
|
|
2
|
+
the whole input space, not just the handful of fixed examples the other test
|
|
3
|
+
files check -- exact physical/mathematical identities that must hold no
|
|
4
|
+
matter what parameters or initial conditions are chosen.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
from hypothesis import given, settings
|
|
11
|
+
from hypothesis import strategies as st
|
|
12
|
+
|
|
13
|
+
from physicskit.chaos.quantum.maps import QuantumBakersMap, QuantumKickedRotor
|
|
14
|
+
from physicskit.chaos.systems.billiards import BunimovichStadium, CircleBilliard, SinaiBilliard
|
|
15
|
+
from physicskit.chaos.systems.maps import HenonMap
|
|
16
|
+
from physicskit.chaos.utils.metrics import numerical_jacobian_map
|
|
17
|
+
from physicskit.chaos.utils.timeseries import delay_embed
|
|
18
|
+
|
|
19
|
+
ALL_BILLIARDS = [
|
|
20
|
+
lambda: CircleBilliard(radius=1.0),
|
|
21
|
+
lambda: SinaiBilliard(cell_size=2.0, scatterer_radius=0.5),
|
|
22
|
+
lambda: BunimovichStadium(radius=1.0, straight_length=2.0),
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
angles = st.floats(min_value=0.0, max_value=2.0 * np.pi, exclude_max=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@settings(deadline=None, max_examples=30)
|
|
29
|
+
@given(billiard_idx=st.sampled_from(range(len(ALL_BILLIARDS))), angle=angles)
|
|
30
|
+
def test_billiard_speed_is_conserved_for_any_launch_angle(billiard_idx, angle):
|
|
31
|
+
"""Specular reflection is elastic: |v| = 1 must hold after every bounce,
|
|
32
|
+
for every billiard shape and every launch angle -- not just the sampled
|
|
33
|
+
angles the example-based tests happen to use."""
|
|
34
|
+
billiard = ALL_BILLIARDS[billiard_idx]()
|
|
35
|
+
pos = billiard.sample_interior_point()
|
|
36
|
+
vel = np.array([np.cos(angle), np.sin(angle)])
|
|
37
|
+
result = billiard.simulate(pos, vel, n_bounces=50)
|
|
38
|
+
speeds = np.hypot(result["vx"], result["vy"])
|
|
39
|
+
np.testing.assert_allclose(speeds, 1.0, atol=1e-8)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@settings(deadline=None, max_examples=30)
|
|
43
|
+
@given(billiard_idx=st.sampled_from(range(len(ALL_BILLIARDS))), angle=angles)
|
|
44
|
+
def test_billiard_boundary_coordinate_s_stays_within_perimeter(billiard_idx, angle):
|
|
45
|
+
"""The arclength coordinate s must always lie in [0, perimeter), for any
|
|
46
|
+
launch angle."""
|
|
47
|
+
billiard = ALL_BILLIARDS[billiard_idx]()
|
|
48
|
+
pos = billiard.sample_interior_point()
|
|
49
|
+
vel = np.array([np.cos(angle), np.sin(angle)])
|
|
50
|
+
result = billiard.simulate(pos, vel, n_bounces=50)
|
|
51
|
+
assert np.all(result["s"] >= 0.0)
|
|
52
|
+
assert np.all(result["s"] <= billiard.perimeter() + 1e-9)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@settings(deadline=None, max_examples=50)
|
|
56
|
+
@given(
|
|
57
|
+
a=st.floats(min_value=0.5, max_value=2.0),
|
|
58
|
+
b=st.floats(min_value=-0.9, max_value=0.9, allow_nan=False).filter(lambda v: abs(v) > 1e-3),
|
|
59
|
+
x=st.floats(min_value=-1.0, max_value=1.0),
|
|
60
|
+
y=st.floats(min_value=-1.0, max_value=1.0),
|
|
61
|
+
)
|
|
62
|
+
def test_henon_map_jacobian_matches_analytic_formula_everywhere(a, b, x, y):
|
|
63
|
+
"""The Henon map's Jacobian is exactly [[-2*a*x, 1], [b, 0]] at every
|
|
64
|
+
point and every parameter choice, not just the one example the
|
|
65
|
+
fixed-value test checks."""
|
|
66
|
+
henon = HenonMap(a=a, b=b)
|
|
67
|
+
jac = numerical_jacobian_map(henon, np.array([x, y]))
|
|
68
|
+
expected = np.array([[-2.0 * a * x, 1.0], [b, 0.0]])
|
|
69
|
+
np.testing.assert_allclose(jac, expected, atol=1e-4, rtol=1e-4)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@settings(deadline=None, max_examples=50)
|
|
73
|
+
@given(
|
|
74
|
+
n=st.integers(min_value=5, max_value=200),
|
|
75
|
+
dim=st.integers(min_value=1, max_value=4),
|
|
76
|
+
tau=st.integers(min_value=1, max_value=5),
|
|
77
|
+
)
|
|
78
|
+
def test_delay_embed_shape_formula_holds_for_any_valid_dim_and_tau(n, dim, tau):
|
|
79
|
+
"""delay_embed's output row count must always be n - (dim - 1) * tau,
|
|
80
|
+
for any series length, dimension, and delay for which that's positive."""
|
|
81
|
+
n_out = n - (dim - 1) * tau
|
|
82
|
+
if n_out <= 0:
|
|
83
|
+
return
|
|
84
|
+
series = np.arange(n, dtype=np.float64)
|
|
85
|
+
embedded = delay_embed(series, dim=dim, tau=tau)
|
|
86
|
+
assert embedded.shape == (n_out, dim)
|
|
87
|
+
# Each row must be an arithmetic progression with common difference tau,
|
|
88
|
+
# since the series itself is just np.arange(n).
|
|
89
|
+
if dim > 1:
|
|
90
|
+
diffs = np.diff(embedded, axis=1)
|
|
91
|
+
np.testing.assert_allclose(diffs, float(tau))
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@settings(deadline=None, max_examples=30)
|
|
95
|
+
@given(k=st.floats(min_value=0.0, max_value=10.0), dim=st.integers(min_value=2, max_value=60))
|
|
96
|
+
def test_quantum_kicked_rotor_floquet_operator_is_unitary_for_any_k_and_dim(k, dim):
|
|
97
|
+
"""The Floquet operator's unitarity is an exact mathematical consequence of
|
|
98
|
+
being built from products of unitary (DFT and diagonal phase) matrices --
|
|
99
|
+
it must hold regardless of the kick strength or Hilbert space dimension."""
|
|
100
|
+
u = QuantumKickedRotor(k=k, dim=dim).floquet_operator()
|
|
101
|
+
np.testing.assert_allclose(u.conj().T @ u, np.eye(dim), atol=1e-8)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@settings(deadline=None, max_examples=30)
|
|
105
|
+
@given(
|
|
106
|
+
dim=st.integers(min_value=4, max_value=60),
|
|
107
|
+
alpha=st.floats(min_value=0.1, max_value=0.9, allow_nan=False),
|
|
108
|
+
)
|
|
109
|
+
def test_quantum_bakers_map_floquet_operator_is_unitary_for_any_dim_and_alpha(dim, alpha):
|
|
110
|
+
"""Same unitarity guarantee as the kicked rotor, for any (dim, alpha) pair
|
|
111
|
+
that yields a valid two-block split."""
|
|
112
|
+
n1 = round(alpha * dim)
|
|
113
|
+
if n1 < 1 or n1 > dim - 1:
|
|
114
|
+
return
|
|
115
|
+
u = QuantumBakersMap(dim=dim, alpha=alpha).floquet_operator()
|
|
116
|
+
np.testing.assert_allclose(u.conj().T @ u, np.eye(dim), atol=1e-8)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.chaos.quantum.billiards import QuantumBilliard, points_in_billiard
|
|
5
|
+
from physicskit.chaos.systems.billiards import (
|
|
6
|
+
BunimovichStadium,
|
|
7
|
+
CircleBilliard,
|
|
8
|
+
RectangleBilliard,
|
|
9
|
+
SinaiBilliard,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_points_in_billiard_excludes_scatterer_hole():
|
|
14
|
+
"""The Sinai billiard's boundary polyline has two components (outer square,
|
|
15
|
+
inner scatterer circle); a correct point-in-domain test must exclude the
|
|
16
|
+
scatterer's interior even though it is "inside" the outer square."""
|
|
17
|
+
billiard = SinaiBilliard(cell_size=2.0, scatterer_radius=0.5)
|
|
18
|
+
points = np.array(
|
|
19
|
+
[
|
|
20
|
+
[0.0, 0.0], # scatterer center: outside the physical domain
|
|
21
|
+
[0.3, 0.0], # inside the scatterer: outside the physical domain
|
|
22
|
+
[0.7, 0.0], # outside the scatterer, inside the cell: inside
|
|
23
|
+
[0.9, 0.9], # near a corner: inside
|
|
24
|
+
[1.5, 1.5], # outside the cell entirely: outside
|
|
25
|
+
]
|
|
26
|
+
)
|
|
27
|
+
expected = np.array([False, False, True, True, False])
|
|
28
|
+
assert np.array_equal(points_in_billiard(billiard, points), expected)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_points_in_billiard_circle():
|
|
32
|
+
billiard = CircleBilliard(radius=1.0)
|
|
33
|
+
points = np.array([[0.0, 0.0], [0.5, 0.5], [0.99, 0.0], [1.01, 0.0], [2.0, 2.0]])
|
|
34
|
+
expected = np.array([True, True, True, False, False])
|
|
35
|
+
assert np.array_equal(points_in_billiard(billiard, points), expected)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_quantum_billiard_rejects_low_resolution():
|
|
39
|
+
with pytest.raises(ValueError):
|
|
40
|
+
QuantumBilliard(CircleBilliard(radius=1.0), resolution=5)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_quantum_billiard_eigenvalues_are_positive_and_ascending():
|
|
44
|
+
qb = QuantumBilliard(RectangleBilliard(width=2.0, height=1.0), resolution=80)
|
|
45
|
+
eigenvalues, eigenfunctions = qb.eigenstates(n_states=6)
|
|
46
|
+
assert eigenvalues.shape == (6,)
|
|
47
|
+
assert np.all(eigenvalues > 0.0)
|
|
48
|
+
assert np.all(np.diff(eigenvalues) >= 0.0)
|
|
49
|
+
assert eigenfunctions.shape == (6, *qb.grid()[0].shape)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_quantum_billiard_eigenfunctions_vanish_outside_domain():
|
|
53
|
+
qb = QuantumBilliard(CircleBilliard(radius=1.0), resolution=80)
|
|
54
|
+
_, eigenfunctions = qb.eigenstates(n_states=3)
|
|
55
|
+
inside = qb._inside
|
|
56
|
+
assert np.all(np.isnan(eigenfunctions[:, ~inside]))
|
|
57
|
+
assert np.all(np.isfinite(eigenfunctions[:, inside]))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_quantum_billiard_matches_analytic_rectangle_spectrum():
|
|
61
|
+
"""The Dirichlet rectangle's eigenvalues are known exactly:
|
|
62
|
+
k^2_mn = pi^2 * (m^2/Lx^2 + n^2/Ly^2). The finite-difference solver should
|
|
63
|
+
match this to a few percent at a moderate grid resolution."""
|
|
64
|
+
lx, ly = 2.0, 1.0
|
|
65
|
+
qb = QuantumBilliard(RectangleBilliard(width=lx, height=ly), resolution=150)
|
|
66
|
+
eigenvalues, _ = qb.eigenstates(n_states=6)
|
|
67
|
+
|
|
68
|
+
exact = sorted(np.pi**2 * (m**2 / lx**2 + n**2 / ly**2) for m in range(1, 8) for n in range(1, 8))[:6]
|
|
69
|
+
|
|
70
|
+
rel_err = np.abs(eigenvalues - np.array(exact)) / np.array(exact)
|
|
71
|
+
assert np.all(rel_err < 0.05)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_quantum_billiard_weyl_law_matches_actual_counts_reasonably():
|
|
75
|
+
"""Weyl's law is an asymptotic average, not exact for any individual
|
|
76
|
+
eigenvalue, but the smooth counting function should sit close to the
|
|
77
|
+
actual staircase over a modest range of low-lying states."""
|
|
78
|
+
qb = QuantumBilliard(BunimovichStadium(radius=1.0, straight_length=2.0), resolution=110)
|
|
79
|
+
n_states = 20
|
|
80
|
+
wavenumbers = qb.wavenumbers(n_states)
|
|
81
|
+
actual_counts = np.arange(1, n_states + 1)
|
|
82
|
+
predicted_counts = qb.weyl_counting_function(wavenumbers)
|
|
83
|
+
assert np.mean(np.abs(actual_counts - predicted_counts)) < 3.0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def test_quantum_billiard_wavenumbers_are_sqrt_of_eigenvalues():
|
|
87
|
+
qb = QuantumBilliard(CircleBilliard(radius=1.0), resolution=80)
|
|
88
|
+
eigenvalues, _ = qb.eigenstates(4)
|
|
89
|
+
assert np.allclose(qb.wavenumbers(4), np.sqrt(eigenvalues))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_quantum_billiard_area_is_close_to_analytic_rectangle_area():
|
|
93
|
+
qb = QuantumBilliard(RectangleBilliard(width=2.0, height=1.5), resolution=150)
|
|
94
|
+
assert qb.area() == pytest.approx(2.0 * 1.5, rel=0.02)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.chaos.quantum.husimi import husimi_function
|
|
5
|
+
from physicskit.chaos.quantum.maps import QuantumBakersMap, QuantumKickedRotor
|
|
6
|
+
from physicskit.chaos.systems.maps import BakersMap, StandardMap
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _is_unitary(u, atol=1e-10):
|
|
10
|
+
n = u.shape[0]
|
|
11
|
+
return np.allclose(u.conj().T @ u, np.eye(n), atol=atol)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize("dim", [8, 33, 64])
|
|
15
|
+
def test_kicked_rotor_floquet_operator_is_unitary(dim):
|
|
16
|
+
qkr = QuantumKickedRotor(k=1.7, dim=dim)
|
|
17
|
+
assert _is_unitary(qkr.floquet_operator())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_kicked_rotor_rejects_too_small_dim():
|
|
21
|
+
with pytest.raises(ValueError):
|
|
22
|
+
QuantumKickedRotor(dim=1)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_kicked_rotor_default_hbar_matches_torus_quantization():
|
|
26
|
+
qkr = QuantumKickedRotor(dim=100)
|
|
27
|
+
assert qkr.hbar == pytest.approx(2.0 * np.pi / 100)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_kicked_rotor_evolve_preserves_norm_and_dimension():
|
|
31
|
+
qkr = QuantumKickedRotor(k=2.0, dim=50)
|
|
32
|
+
psi0 = qkr.coherent_state(1.0, 2.0)
|
|
33
|
+
states = qkr.evolve(psi0, n_steps=10)
|
|
34
|
+
assert states.shape == (11, 50)
|
|
35
|
+
assert np.allclose(np.linalg.norm(states, axis=1), 1.0)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_kicked_rotor_eigenphases_are_on_unit_circle():
|
|
39
|
+
qkr = QuantumKickedRotor(k=3.0, dim=40)
|
|
40
|
+
phases = qkr.eigenphases()
|
|
41
|
+
assert phases.shape == (40,)
|
|
42
|
+
assert np.all(phases >= -np.pi) and np.all(phases <= np.pi)
|
|
43
|
+
assert np.all(np.diff(phases) >= 0.0)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_kicked_rotor_coherent_state_husimi_peaks_at_its_own_center():
|
|
47
|
+
qkr = QuantumKickedRotor(k=1.0, dim=300)
|
|
48
|
+
theta0, p0 = 2.3, 4.1
|
|
49
|
+
psi = qkr.coherent_state(theta0, p0)
|
|
50
|
+
theta_grid, p_grid, husimi = qkr.husimi(psi, resolution=150)
|
|
51
|
+
idx = np.unravel_index(np.argmax(husimi), husimi.shape)
|
|
52
|
+
assert theta_grid[idx] == pytest.approx(theta0, abs=0.1)
|
|
53
|
+
assert p_grid[idx] == pytest.approx(p0, abs=0.1)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.mark.slow
|
|
57
|
+
def test_kicked_rotor_husimi_peak_tracks_classical_orbit_at_weak_kick():
|
|
58
|
+
"""At small k the classical map is near-integrable, so a narrow wavepacket's
|
|
59
|
+
Husimi peak should closely track the classical StandardMap orbit for a
|
|
60
|
+
handful of iterations, before it has a chance to spread appreciably."""
|
|
61
|
+
k = 0.3
|
|
62
|
+
dim = 200
|
|
63
|
+
theta0, p0 = 1.0, 1.0
|
|
64
|
+
qkr = QuantumKickedRotor(k=k, dim=dim)
|
|
65
|
+
psi = qkr.coherent_state(theta0, p0)
|
|
66
|
+
states = qkr.evolve(psi, n_steps=4)
|
|
67
|
+
|
|
68
|
+
classical = StandardMap(k=k).trajectory(np.array([theta0, p0]), n_iter=4)
|
|
69
|
+
|
|
70
|
+
for i in range(5):
|
|
71
|
+
theta_grid, p_grid, husimi = qkr.husimi(states[i], resolution=80)
|
|
72
|
+
idx = np.unravel_index(np.argmax(husimi), husimi.shape)
|
|
73
|
+
assert theta_grid[idx] == pytest.approx(classical[i, 0], abs=0.15)
|
|
74
|
+
assert p_grid[idx] == pytest.approx(classical[i, 1], abs=0.15)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("dim", [8, 32, 100])
|
|
78
|
+
def test_bakers_map_floquet_operator_is_unitary_symmetric(dim):
|
|
79
|
+
qbm = QuantumBakersMap(dim=dim, alpha=0.5)
|
|
80
|
+
assert _is_unitary(qbm.floquet_operator())
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@pytest.mark.parametrize("dim,alpha", [(10, 0.3), (100, 0.7), (99, 1.0 / 3.0)])
|
|
84
|
+
def test_bakers_map_floquet_operator_is_unitary_asymmetric(dim, alpha):
|
|
85
|
+
qbm = QuantumBakersMap(dim=dim, alpha=alpha)
|
|
86
|
+
assert _is_unitary(qbm.floquet_operator())
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_bakers_map_rejects_invalid_alpha():
|
|
90
|
+
with pytest.raises(ValueError):
|
|
91
|
+
QuantumBakersMap(dim=10, alpha=0.0)
|
|
92
|
+
with pytest.raises(ValueError):
|
|
93
|
+
QuantumBakersMap(dim=10, alpha=1.0)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def test_bakers_map_rejects_dim_alpha_combo_with_empty_block():
|
|
97
|
+
with pytest.raises(ValueError):
|
|
98
|
+
QuantumBakersMap(dim=10, alpha=0.02)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_bakers_map_evolve_preserves_norm_and_dimension():
|
|
102
|
+
qbm = QuantumBakersMap(dim=48)
|
|
103
|
+
psi0 = qbm.coherent_state(0.2, 0.6)
|
|
104
|
+
states = qbm.evolve(psi0, n_steps=6)
|
|
105
|
+
assert states.shape == (7, 48)
|
|
106
|
+
assert np.allclose(np.linalg.norm(states, axis=1), 1.0)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_bakers_map_coherent_state_husimi_peaks_at_its_own_center():
|
|
110
|
+
qbm = QuantumBakersMap(dim=250)
|
|
111
|
+
q0, p0 = 0.35, 0.72
|
|
112
|
+
psi = qbm.coherent_state(q0, p0)
|
|
113
|
+
q_grid, p_grid, husimi = qbm.husimi(psi, resolution=150)
|
|
114
|
+
idx = np.unravel_index(np.argmax(husimi), husimi.shape)
|
|
115
|
+
assert q_grid[idx] == pytest.approx(q0, abs=0.03)
|
|
116
|
+
assert p_grid[idx] == pytest.approx(p0, abs=0.03)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_bakers_map_hbar_matches_classical_area_quantization():
|
|
120
|
+
qbm = QuantumBakersMap(dim=40)
|
|
121
|
+
assert qbm.hbar == pytest.approx(1.0 / 40)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_bakers_map_eigenphases_shape_and_range():
|
|
125
|
+
qbm = QuantumBakersMap(dim=40)
|
|
126
|
+
phases = qbm.eigenphases()
|
|
127
|
+
assert phases.shape == (40,)
|
|
128
|
+
assert np.all(np.diff(phases) >= 0.0)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_husimi_function_is_normalized_to_unit_peak():
|
|
132
|
+
n = 64
|
|
133
|
+
psi = np.zeros(n, dtype=complex)
|
|
134
|
+
psi[0] = 1.0
|
|
135
|
+
_, _, husimi = husimi_function(psi, hbar=2.0 * np.pi / n, q_period=2.0 * np.pi, resolution=40)
|
|
136
|
+
assert husimi.max() == pytest.approx(1.0)
|
|
137
|
+
assert husimi.min() >= 0.0
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_classical_bakers_map_default_alpha_matches_quantum_default():
|
|
141
|
+
"""physicskit.chaos's classical BakersMap defaults to alpha=0.5; the quantum
|
|
142
|
+
version's default should match, so QuantumBakersMap(dim) quantizes
|
|
143
|
+
BakersMap() out of the box."""
|
|
144
|
+
assert BakersMap().alpha == QuantumBakersMap(dim=10).alpha
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.chaos.systems.maps import HenonMap
|
|
5
|
+
from physicskit.chaos.utils.recurrence import kac_lemma_estimate, recurrence_matrix, recurrence_times
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_recurrence_matrix_is_symmetric_with_true_diagonal():
|
|
9
|
+
rng = np.random.default_rng(0)
|
|
10
|
+
states = rng.uniform(size=(50, 2))
|
|
11
|
+
matrix = recurrence_matrix(states, epsilon=0.3)
|
|
12
|
+
assert matrix.shape == (50, 50)
|
|
13
|
+
assert matrix.dtype == np.bool_
|
|
14
|
+
np.testing.assert_array_equal(matrix, matrix.T)
|
|
15
|
+
assert np.all(np.diag(matrix)) # every point recurs to itself at distance 0
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_recurrence_matrix_epsilon_zero_is_only_the_diagonal():
|
|
19
|
+
rng = np.random.default_rng(0)
|
|
20
|
+
states = rng.uniform(size=(20, 2))
|
|
21
|
+
matrix = recurrence_matrix(states, epsilon=1e-12)
|
|
22
|
+
np.testing.assert_array_equal(matrix, np.eye(20, dtype=bool))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_recurrence_times_detects_a_simple_periodic_orbit():
|
|
26
|
+
"""A trajectory that revisits the same three points in a cycle should
|
|
27
|
+
show a recurrence time of exactly the period (3 steps) every time."""
|
|
28
|
+
states = np.tile(np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]), (10, 1))
|
|
29
|
+
times = recurrence_times(states, epsilon=0.01, reference_idx=0, dt=1.0)
|
|
30
|
+
assert times.size > 0
|
|
31
|
+
np.testing.assert_allclose(times, 3.0)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_recurrence_times_scales_with_dt():
|
|
35
|
+
states = np.tile(np.array([[0.0, 0.0], [1.0, 0.0], [2.0, 0.0]]), (10, 1))
|
|
36
|
+
times_dt1 = recurrence_times(states, epsilon=0.01, dt=1.0)
|
|
37
|
+
times_dt2 = recurrence_times(states, epsilon=0.01, dt=0.5)
|
|
38
|
+
np.testing.assert_allclose(times_dt2, times_dt1 * 0.5)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_kac_lemma_holds_for_a_long_henon_trajectory():
|
|
42
|
+
"""Kac's lemma predicts mean_recurrence_time * measure(epsilon-ball) ~= 1
|
|
43
|
+
for an ergodic, measure-preserving trajectory sampled long enough."""
|
|
44
|
+
system = HenonMap(a=1.4, b=0.3)
|
|
45
|
+
traj = system.trajectory(np.array([0.1, 0.1]), n_iter=100000)
|
|
46
|
+
traj = traj[500:] # discard transient
|
|
47
|
+
|
|
48
|
+
mean_time, measure, kac_product = kac_lemma_estimate(traj, epsilon=0.05, reference_idx=0)
|
|
49
|
+
assert mean_time > 0.0
|
|
50
|
+
assert 0.0 < measure < 1.0
|
|
51
|
+
assert kac_product == pytest.approx(1.0, abs=0.1)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import pytest
|
|
3
|
+
|
|
4
|
+
from physicskit.chaos.core.base_system import DiscreteMap, DynamicalSystem, get_init_params
|
|
5
|
+
from physicskit.chaos.exceptions import ChaoskitError, InvalidParameterError
|
|
6
|
+
from physicskit.chaos.quantum.billiards import QuantumBilliard
|
|
7
|
+
from physicskit.chaos.quantum.maps import QuantumBakersMap, QuantumKickedRotor
|
|
8
|
+
from physicskit.chaos.systems.billiards import CircleBilliard, EllipseBilliard, SinaiBilliard
|
|
9
|
+
from physicskit.chaos.systems.continuous import Lorenz, MagneticPendulum
|
|
10
|
+
from physicskit.chaos.systems.maps import BakersMap, HenonMap, LogisticMap, StandardMap
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.mark.parametrize(
|
|
14
|
+
"obj, expected_substrings",
|
|
15
|
+
[
|
|
16
|
+
(Lorenz(sigma=10.0, rho=28.0), ["Lorenz(", "sigma=10.0", "rho=28.0"]),
|
|
17
|
+
(HenonMap(a=1.4, b=0.3), ["HenonMap(", "a=1.4", "b=0.3"]),
|
|
18
|
+
(SinaiBilliard(cell_size=2.0), ["SinaiBilliard(", "cell_size=2.0"]),
|
|
19
|
+
(MagneticPendulum(friction=0.3), ["MagneticPendulum(", "friction=0.3"]),
|
|
20
|
+
(QuantumKickedRotor(k=2.0, dim=32), ["QuantumKickedRotor(", "k=2.0", "dim=32"]),
|
|
21
|
+
(QuantumBakersMap(dim=40, alpha=0.5), ["QuantumBakersMap(", "dim=40", "alpha=0.5"]),
|
|
22
|
+
(
|
|
23
|
+
QuantumBilliard(CircleBilliard(radius=1.0), resolution=50),
|
|
24
|
+
["QuantumBilliard(", "resolution=50"],
|
|
25
|
+
),
|
|
26
|
+
],
|
|
27
|
+
)
|
|
28
|
+
def test_repr_includes_class_name_and_init_params(obj, expected_substrings):
|
|
29
|
+
text = repr(obj)
|
|
30
|
+
for substring in expected_substrings:
|
|
31
|
+
assert substring in text
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_repr_round_trips_through_eval_for_simple_scalar_params():
|
|
35
|
+
"""For classes whose __init__ params are all simple scalars, repr()
|
|
36
|
+
should produce a string that reconstructs an equivalent object."""
|
|
37
|
+
original = HenonMap(a=1.4, b=0.3)
|
|
38
|
+
reconstructed = eval(repr(original), {"HenonMap": HenonMap})
|
|
39
|
+
assert reconstructed.a == original.a
|
|
40
|
+
assert reconstructed.b == original.b
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize(
|
|
44
|
+
"make_map",
|
|
45
|
+
[lambda: StandardMap(), lambda: HenonMap(), lambda: BakersMap(), lambda: LogisticMap()],
|
|
46
|
+
)
|
|
47
|
+
def test_discrete_map_trajectory_defaults_state0_like_dynamical_system(make_map):
|
|
48
|
+
"""DiscreteMap.trajectory() should default state0 via initial_state(),
|
|
49
|
+
exactly like DynamicalSystem.trajectory() already does -- not require it
|
|
50
|
+
positionally."""
|
|
51
|
+
system = make_map()
|
|
52
|
+
traj = system.trajectory(n_iter=5)
|
|
53
|
+
assert traj.shape == (6, system.dim)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_invalid_parameter_error_is_both_chaos_and_value_error():
|
|
57
|
+
with pytest.raises(ChaoskitError):
|
|
58
|
+
EllipseBilliard(semi_major=1.0, semi_minor=1.5)
|
|
59
|
+
with pytest.raises(ValueError):
|
|
60
|
+
EllipseBilliard(semi_major=1.0, semi_minor=1.5)
|
|
61
|
+
with pytest.raises(InvalidParameterError):
|
|
62
|
+
EllipseBilliard(semi_major=1.0, semi_minor=1.5)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class _MinimalDynamicalSystem(DynamicalSystem):
|
|
66
|
+
"""The smallest possible concrete subclass: overrides only the
|
|
67
|
+
abstract ``rhs``, so calling ``initial_state()`` exercises
|
|
68
|
+
DynamicalSystem's own default (every real system in this package
|
|
69
|
+
overrides it instead, per module docstring)."""
|
|
70
|
+
|
|
71
|
+
dim = 2
|
|
72
|
+
|
|
73
|
+
def rhs(self, state, t):
|
|
74
|
+
return -state
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class _MinimalDiscreteMap(DiscreteMap):
|
|
78
|
+
"""The smallest possible concrete subclass: overrides only the
|
|
79
|
+
abstract ``step``, so calling ``initial_state()``/``trajectory()``
|
|
80
|
+
exercises DiscreteMap's own defaults (every real map in this package
|
|
81
|
+
overrides both, per module docstring)."""
|
|
82
|
+
|
|
83
|
+
dim = 1
|
|
84
|
+
|
|
85
|
+
def step(self, state):
|
|
86
|
+
return state + 1.0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_dynamical_system_initial_state_default_raises_not_implemented():
|
|
90
|
+
with pytest.raises(NotImplementedError):
|
|
91
|
+
_MinimalDynamicalSystem().initial_state()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_discrete_map_initial_state_default_raises_not_implemented():
|
|
95
|
+
with pytest.raises(NotImplementedError):
|
|
96
|
+
_MinimalDiscreteMap().initial_state()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def test_discrete_map_trajectory_default_implementation_iterates_step():
|
|
100
|
+
system = _MinimalDiscreteMap()
|
|
101
|
+
traj = system.trajectory(state0=np.array([0.0]), n_iter=3)
|
|
102
|
+
np.testing.assert_allclose(traj, [[0.0], [1.0], [2.0], [3.0]])
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_get_init_params_skips_unreadable_signature_and_missing_attributes():
|
|
106
|
+
"""get_init_params must degrade gracefully both when the constructor's
|
|
107
|
+
signature can't be introspected (returns {}) and when a constructor
|
|
108
|
+
parameter isn't stored under its own name on the instance (that one
|
|
109
|
+
param is skipped, not raised)."""
|
|
110
|
+
|
|
111
|
+
class Skippy:
|
|
112
|
+
def __init__(self, a, b=1):
|
|
113
|
+
self.a = a
|
|
114
|
+
# b intentionally not stored as self.b
|
|
115
|
+
|
|
116
|
+
obj = Skippy(a=1, b=2)
|
|
117
|
+
assert get_init_params(obj) == {"a": 1}
|
|
118
|
+
|
|
119
|
+
class Unreadable:
|
|
120
|
+
def __init__(self, a=1):
|
|
121
|
+
self.a = a
|
|
122
|
+
|
|
123
|
+
unreadable_obj = Unreadable(a=5)
|
|
124
|
+
Unreadable.__init__ = 5 # no longer a real callable/function
|
|
125
|
+
assert get_init_params(unreadable_obj) == {}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_billiard_boundary_arrays_matches_build_boundary_output():
|
|
129
|
+
billiard = CircleBilliard(radius=2.0)
|
|
130
|
+
segments, arcs, arc_full = billiard.boundary_arrays()
|
|
131
|
+
assert segments.shape == (0, 4)
|
|
132
|
+
assert arcs.shape == (1, 5)
|
|
133
|
+
assert arc_full.tolist() == [True]
|
|
134
|
+
np.testing.assert_allclose(arcs[0][:3], [0.0, 0.0, 2.0])
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
from physicskit.chaos.systems.continuous import RestrictedThreeBody
|
|
4
|
+
from physicskit.chaos.visualizers.section import plot_poincare_map, poincare_crossings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_poincare_crossings_only_keeps_the_requested_direction():
|
|
8
|
+
"""Every returned crossing must have the section coordinate increasing
|
|
9
|
+
through `value` when direction=1.0 (and decreasing for direction=-1.0)."""
|
|
10
|
+
system = RestrictedThreeBody()
|
|
11
|
+
state0 = system.initial_state()
|
|
12
|
+
|
|
13
|
+
up = poincare_crossings(system, state0, coord=1, value=0.0, direction=1.0, t_max=2.0 * 17.0652165601579625588917206249, dt=1e-4)
|
|
14
|
+
down = poincare_crossings(system, state0, coord=1, value=0.0, direction=-1.0, t_max=2.0 * 17.0652165601579625588917206249, dt=1e-4)
|
|
15
|
+
|
|
16
|
+
assert up.shape[0] > 0
|
|
17
|
+
assert down.shape[0] > 0
|
|
18
|
+
# Interpolated crossings should sit essentially exactly on the surface.
|
|
19
|
+
np.testing.assert_allclose(up[:, 1], 0.0, atol=1e-6)
|
|
20
|
+
np.testing.assert_allclose(down[:, 1], 0.0, atol=1e-6)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_poincare_crossings_returns_empty_array_for_a_surface_never_reached():
|
|
24
|
+
system = RestrictedThreeBody()
|
|
25
|
+
state0 = system.initial_state()
|
|
26
|
+
crossings = poincare_crossings(system, state0, coord=1, value=100.0, t_max=1.0, dt=1e-3)
|
|
27
|
+
assert crossings.shape == (0, system.dim)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_plot_poincare_map_draws_one_scatter_series_per_initial_state():
|
|
31
|
+
system = RestrictedThreeBody()
|
|
32
|
+
state0 = system.initial_state()
|
|
33
|
+
state0_perturbed = state0.copy()
|
|
34
|
+
state0_perturbed[3] *= 1.001
|
|
35
|
+
|
|
36
|
+
_, ax = plot_poincare_map(
|
|
37
|
+
system,
|
|
38
|
+
[state0, state0_perturbed],
|
|
39
|
+
coord=1,
|
|
40
|
+
value=0.0,
|
|
41
|
+
direction=1.0,
|
|
42
|
+
t_max=2.0 * 17.0652165601579625588917206249,
|
|
43
|
+
dt=1e-4,
|
|
44
|
+
labels=["reference", "perturbed"],
|
|
45
|
+
)
|
|
46
|
+
assert len(ax.collections) == 2
|
|
47
|
+
legend = ax.get_legend()
|
|
48
|
+
assert legend is not None
|
|
49
|
+
assert [text.get_text() for text in legend.get_texts()] == ["reference", "perturbed"]
|