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,271 @@
|
|
|
1
|
+
r"""Split-operator FFT propagators for the time-dependent Schrodinger equation.
|
|
2
|
+
|
|
3
|
+
Both solvers implement the symmetric (Strang) splitting
|
|
4
|
+
|
|
5
|
+
.. math::
|
|
6
|
+
|
|
7
|
+
\psi(t+dt) = e^{-i \hat V dt / 2\hbar}\, e^{-i \hat T dt/\hbar}\,
|
|
8
|
+
e^{-i \hat V dt / 2\hbar}\, \psi(t),
|
|
9
|
+
|
|
10
|
+
which is unitary to machine precision regardless of :math:`dt` (probability
|
|
11
|
+
is exactly conserved up to FFT/round-off error), and second-order accurate
|
|
12
|
+
in :math:`dt`.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
|
|
21
|
+
from .._compat import trapz
|
|
22
|
+
|
|
23
|
+
__all__ = ["SplitOperatorSolver1D", "SplitOperatorSolver2D"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SplitOperatorSolver1D:
|
|
27
|
+
r"""Propagate a 1D wavepacket under potential :math:`V(x)` or :math:`V(x,t)`.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
x : numpy.ndarray
|
|
32
|
+
Uniform spatial grid.
|
|
33
|
+
V : callable or array_like
|
|
34
|
+
Potential energy: a function ``V(x)`` (static) or ``V(x, t)``
|
|
35
|
+
(time-dependent, detected by inspecting the function's signature),
|
|
36
|
+
or a precomputed array matching ``x``.
|
|
37
|
+
hbar : float, default=1.0
|
|
38
|
+
Value of :math:`\hbar` to use.
|
|
39
|
+
m : float, default=1.0
|
|
40
|
+
Particle mass.
|
|
41
|
+
dt : float, default=1e-3
|
|
42
|
+
Time step.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, x: np.ndarray, V, hbar: float = 1.0, m: float = 1.0, dt: float = 1e-3):
|
|
46
|
+
self.x = np.asarray(x, dtype=float)
|
|
47
|
+
self.N = self.x.shape[0]
|
|
48
|
+
self.dx = self.x[1] - self.x[0]
|
|
49
|
+
self.hbar = hbar
|
|
50
|
+
self.m = m
|
|
51
|
+
self.dt = dt
|
|
52
|
+
self._V = V
|
|
53
|
+
self._time_dependent = callable(V) and _accepts_time(V, n_spatial_args=1)
|
|
54
|
+
|
|
55
|
+
self.k = 2 * np.pi * np.fft.fftfreq(self.N, d=self.dx)
|
|
56
|
+
self._kin_phase = np.exp(-1j * hbar * self.k**2 * dt / (2 * m))
|
|
57
|
+
|
|
58
|
+
if not self._time_dependent:
|
|
59
|
+
V0 = V(self.x) if callable(V) else np.asarray(V, dtype=float)
|
|
60
|
+
self._half_pot_phase = np.exp(-1j * V0 * dt / (2 * hbar))
|
|
61
|
+
else:
|
|
62
|
+
self._half_pot_phase = None
|
|
63
|
+
|
|
64
|
+
def _half_potential_phase(self, t: float) -> np.ndarray:
|
|
65
|
+
if self._half_pot_phase is not None:
|
|
66
|
+
return self._half_pot_phase
|
|
67
|
+
Vt = self._V(self.x, t)
|
|
68
|
+
return np.exp(-1j * Vt * self.dt / (2 * self.hbar))
|
|
69
|
+
|
|
70
|
+
def step(self, psi: np.ndarray, t: float = 0.0) -> np.ndarray:
|
|
71
|
+
"""Advance the wavefunction by one time step ``dt``.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
psi : numpy.ndarray
|
|
76
|
+
Complex wavefunction at time ``t``, sampled on ``self.x``.
|
|
77
|
+
t : float, default=0.0
|
|
78
|
+
Current time (only used if the potential is time-dependent).
|
|
79
|
+
|
|
80
|
+
Returns
|
|
81
|
+
-------
|
|
82
|
+
numpy.ndarray
|
|
83
|
+
The wavefunction at time ``t + dt``.
|
|
84
|
+
"""
|
|
85
|
+
psi = psi * self._half_potential_phase(t)
|
|
86
|
+
psi = np.fft.ifft(self._kin_phase * np.fft.fft(psi))
|
|
87
|
+
psi = psi * self._half_potential_phase(t + self.dt)
|
|
88
|
+
return psi
|
|
89
|
+
|
|
90
|
+
def propagate(self, psi0: np.ndarray, n_steps: int, t0: float = 0.0, save_every: int = 1) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
|
|
91
|
+
"""Propagate an initial state and record its history.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
psi0 : numpy.ndarray
|
|
96
|
+
Initial wavefunction (renormalized internally).
|
|
97
|
+
n_steps : int
|
|
98
|
+
Total number of time steps to take.
|
|
99
|
+
t0 : float, default=0.0
|
|
100
|
+
Initial time.
|
|
101
|
+
save_every : int, default=1
|
|
102
|
+
Save a snapshot every ``save_every`` steps (plus the initial state).
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
frames : numpy.ndarray
|
|
107
|
+
Stacked wavefunction snapshots, shape ``(n_saved, len(x))``.
|
|
108
|
+
times : numpy.ndarray
|
|
109
|
+
The time of each snapshot, shape ``(n_saved,)``.
|
|
110
|
+
"""
|
|
111
|
+
psi = np.asarray(psi0, dtype=complex).copy()
|
|
112
|
+
psi /= np.sqrt(trapz(np.abs(psi) ** 2, self.x))
|
|
113
|
+
|
|
114
|
+
frames = [psi.copy()]
|
|
115
|
+
times = [t0]
|
|
116
|
+
t = t0
|
|
117
|
+
for step_idx in range(1, n_steps + 1):
|
|
118
|
+
psi = self.step(psi, t)
|
|
119
|
+
t = t0 + step_idx * self.dt
|
|
120
|
+
if step_idx % save_every == 0:
|
|
121
|
+
frames.append(psi.copy())
|
|
122
|
+
times.append(t)
|
|
123
|
+
return np.array(frames), np.array(times)
|
|
124
|
+
|
|
125
|
+
def norm(self, psi: np.ndarray) -> float:
|
|
126
|
+
r"""Total probability :math:`\int \lvert\psi(x)\rvert^2\,dx`.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
psi : numpy.ndarray
|
|
131
|
+
Wavefunction sampled on ``self.x``.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
float
|
|
136
|
+
The normalization integral (should stay at 1.0 under unitary
|
|
137
|
+
evolution).
|
|
138
|
+
"""
|
|
139
|
+
return float(trapz(np.abs(psi) ** 2, self.x))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class SplitOperatorSolver2D:
|
|
143
|
+
r"""Propagate a 2D wavepacket under potential :math:`V(x,y)` or :math:`V(x,y,t)`.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
x, y : numpy.ndarray
|
|
148
|
+
Uniform spatial grids along each axis.
|
|
149
|
+
V : callable or array_like
|
|
150
|
+
Potential energy: a function ``V(x, y)`` (static) or ``V(x, y, t)``
|
|
151
|
+
(time-dependent, detected by inspecting the function's signature),
|
|
152
|
+
or a precomputed array matching the ``(x, y)`` meshgrid.
|
|
153
|
+
hbar : float, default=1.0
|
|
154
|
+
Value of :math:`\hbar` to use.
|
|
155
|
+
m : float, default=1.0
|
|
156
|
+
Particle mass.
|
|
157
|
+
dt : float, default=1e-3
|
|
158
|
+
Time step.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(self, x: np.ndarray, y: np.ndarray, V, hbar: float = 1.0, m: float = 1.0, dt: float = 1e-3):
|
|
162
|
+
self.x = np.asarray(x, dtype=float)
|
|
163
|
+
self.y = np.asarray(y, dtype=float)
|
|
164
|
+
self.dx = self.x[1] - self.x[0]
|
|
165
|
+
self.dy = self.y[1] - self.y[0]
|
|
166
|
+
self.hbar = hbar
|
|
167
|
+
self.m = m
|
|
168
|
+
self.dt = dt
|
|
169
|
+
self._V = V
|
|
170
|
+
self._time_dependent = callable(V) and _accepts_time(V, n_spatial_args=2)
|
|
171
|
+
|
|
172
|
+
self.X, self.Y = np.meshgrid(self.x, self.y, indexing="ij")
|
|
173
|
+
kx = 2 * np.pi * np.fft.fftfreq(self.x.shape[0], d=self.dx)
|
|
174
|
+
ky = 2 * np.pi * np.fft.fftfreq(self.y.shape[0], d=self.dy)
|
|
175
|
+
KX, KY = np.meshgrid(kx, ky, indexing="ij")
|
|
176
|
+
self._kin_phase = np.exp(-1j * hbar * (KX**2 + KY**2) * dt / (2 * m))
|
|
177
|
+
|
|
178
|
+
if not self._time_dependent:
|
|
179
|
+
V0 = V(self.X, self.Y) if callable(V) else np.asarray(V, dtype=float)
|
|
180
|
+
self._half_pot_phase = np.exp(-1j * V0 * dt / (2 * hbar))
|
|
181
|
+
else:
|
|
182
|
+
self._half_pot_phase = None
|
|
183
|
+
|
|
184
|
+
def _half_potential_phase(self, t: float) -> np.ndarray:
|
|
185
|
+
if self._half_pot_phase is not None:
|
|
186
|
+
return self._half_pot_phase
|
|
187
|
+
Vt = self._V(self.X, self.Y, t)
|
|
188
|
+
return np.exp(-1j * Vt * self.dt / (2 * self.hbar))
|
|
189
|
+
|
|
190
|
+
def step(self, psi: np.ndarray, t: float = 0.0) -> np.ndarray:
|
|
191
|
+
"""Advance the wavefunction by one time step ``dt``.
|
|
192
|
+
|
|
193
|
+
Parameters
|
|
194
|
+
----------
|
|
195
|
+
psi : numpy.ndarray
|
|
196
|
+
Complex wavefunction at time ``t``, sampled on the ``(x, y)`` grid.
|
|
197
|
+
t : float, default=0.0
|
|
198
|
+
Current time (only used if the potential is time-dependent).
|
|
199
|
+
|
|
200
|
+
Returns
|
|
201
|
+
-------
|
|
202
|
+
numpy.ndarray
|
|
203
|
+
The wavefunction at time ``t + dt``.
|
|
204
|
+
"""
|
|
205
|
+
psi = psi * self._half_potential_phase(t)
|
|
206
|
+
psi = np.fft.ifft2(self._kin_phase * np.fft.fft2(psi))
|
|
207
|
+
psi = psi * self._half_potential_phase(t + self.dt)
|
|
208
|
+
return psi
|
|
209
|
+
|
|
210
|
+
def propagate(self, psi0: np.ndarray, n_steps: int, t0: float = 0.0, save_every: int = 1):
|
|
211
|
+
"""Propagate an initial state and record its history.
|
|
212
|
+
|
|
213
|
+
Parameters
|
|
214
|
+
----------
|
|
215
|
+
psi0 : numpy.ndarray
|
|
216
|
+
Initial wavefunction (renormalized internally).
|
|
217
|
+
n_steps : int
|
|
218
|
+
Total number of time steps to take.
|
|
219
|
+
t0 : float, default=0.0
|
|
220
|
+
Initial time.
|
|
221
|
+
save_every : int, default=1
|
|
222
|
+
Save a snapshot every ``save_every`` steps (plus the initial state).
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
frames : numpy.ndarray
|
|
227
|
+
Stacked wavefunction snapshots, shape ``(n_saved, *X.shape)``.
|
|
228
|
+
times : numpy.ndarray
|
|
229
|
+
The time of each snapshot, shape ``(n_saved,)``.
|
|
230
|
+
"""
|
|
231
|
+
psi = np.asarray(psi0, dtype=complex).copy()
|
|
232
|
+
psi /= np.sqrt(self.norm(psi))
|
|
233
|
+
|
|
234
|
+
frames = [psi.copy()]
|
|
235
|
+
times = [t0]
|
|
236
|
+
t = t0
|
|
237
|
+
for step_idx in range(1, n_steps + 1):
|
|
238
|
+
psi = self.step(psi, t)
|
|
239
|
+
t = t0 + step_idx * self.dt
|
|
240
|
+
if step_idx % save_every == 0:
|
|
241
|
+
frames.append(psi.copy())
|
|
242
|
+
times.append(t)
|
|
243
|
+
return np.array(frames), np.array(times)
|
|
244
|
+
|
|
245
|
+
def norm(self, psi: np.ndarray) -> float:
|
|
246
|
+
r"""Total probability :math:`\iint \lvert\psi(x,y)\rvert^2\,dx\,dy`.
|
|
247
|
+
|
|
248
|
+
Parameters
|
|
249
|
+
----------
|
|
250
|
+
psi : numpy.ndarray
|
|
251
|
+
Wavefunction sampled on the ``(x, y)`` grid.
|
|
252
|
+
|
|
253
|
+
Returns
|
|
254
|
+
-------
|
|
255
|
+
float
|
|
256
|
+
The normalization integral (should stay at 1.0 under unitary
|
|
257
|
+
evolution).
|
|
258
|
+
"""
|
|
259
|
+
density = np.abs(psi) ** 2
|
|
260
|
+
return float(trapz(trapz(density, self.y, axis=1), self.x))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _accepts_time(func: Callable, n_spatial_args: int) -> bool:
|
|
264
|
+
"""True if ``func`` takes more than ``n_spatial_args`` positional
|
|
265
|
+
parameters, i.e. it is V(x, ..., t) rather than V(x, ...)."""
|
|
266
|
+
try:
|
|
267
|
+
import inspect
|
|
268
|
+
|
|
269
|
+
return len(inspect.signature(func).parameters) > n_spatial_args
|
|
270
|
+
except (TypeError, ValueError):
|
|
271
|
+
return False
|
|
File without changes
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""Every dynamical demo produces a genuine frame-by-frame animation (not a
|
|
2
|
+
single static snapshot), and the underlying physics is spot-checked against
|
|
3
|
+
known behavior (spreading, tunneling, splitting, entanglement growth, ...).
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pytest
|
|
8
|
+
from matplotlib.animation import FuncAnimation, PillowWriter
|
|
9
|
+
|
|
10
|
+
from physicskit.quantum._compat import trapz
|
|
11
|
+
from physicskit.quantum.chapters.entanglement import IsingEntangler
|
|
12
|
+
from physicskit.quantum.chapters.harmonic_spin import HarmonicOscillator
|
|
13
|
+
from physicskit.quantum.chapters.hydrogen_am import HydrogenOrbital, orbital_superposition_density
|
|
14
|
+
from physicskit.quantum.chapters.potentials import DoubleWellSimulator, FiniteSquareWell
|
|
15
|
+
from physicskit.quantum.chapters.spin import RabiProblem, SternGerlach
|
|
16
|
+
from physicskit.quantum.chapters.wave_packets import GaussianDispersion, propagate_double_slit
|
|
17
|
+
from physicskit.quantum.visualizers.bloch_sphere import animate_bloch_sphere, state_to_bloch_trajectory
|
|
18
|
+
from physicskit.quantum.visualizers.entanglement import animate_entanglement_growth
|
|
19
|
+
from physicskit.quantum.visualizers.orbitals import animate_orbital_beating
|
|
20
|
+
from physicskit.quantum.visualizers.wavefunctions import animate_density, animate_density_2d
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _save_and_check(anim: FuncAnimation, tmp_path, name: str):
|
|
24
|
+
out = tmp_path / name
|
|
25
|
+
anim.save(out, writer=PillowWriter(fps=10))
|
|
26
|
+
assert out.exists() and out.stat().st_size > 0
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# 1. Double-slit: genuine 2D time propagation through a two-gap wall.
|
|
30
|
+
@pytest.mark.slow
|
|
31
|
+
def test_double_slit_propagation_animates(tmp_path):
|
|
32
|
+
x = np.linspace(-15, 15, 96)
|
|
33
|
+
y = np.linspace(-12, 12, 96)
|
|
34
|
+
X, Y, frames, times = propagate_double_slit(
|
|
35
|
+
x,
|
|
36
|
+
y,
|
|
37
|
+
x0=-8.0,
|
|
38
|
+
k0=8.0,
|
|
39
|
+
sigma_x=0.7,
|
|
40
|
+
sigma_y=4.0,
|
|
41
|
+
wall_x=0.0,
|
|
42
|
+
n_steps=160,
|
|
43
|
+
save_every=20,
|
|
44
|
+
dt=1e-4,
|
|
45
|
+
)
|
|
46
|
+
assert frames.shape[0] == len(times) >= 5
|
|
47
|
+
norm0 = float(np.sum(np.abs(frames[0]) ** 2)) * (x[1] - x[0]) * (y[1] - y[0])
|
|
48
|
+
norm_end = float(np.sum(np.abs(frames[-1]) ** 2)) * (x[1] - x[0]) * (y[1] - y[0])
|
|
49
|
+
assert abs(norm0 - norm_end) < 1e-2 # probability conserved through the slits
|
|
50
|
+
|
|
51
|
+
anim = animate_density_2d(X, Y, frames, times=times)
|
|
52
|
+
_save_and_check(anim, tmp_path, "double_slit.gif")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# 2. Quantum tunnelling: a wavepacket incident on a barrier splits into
|
|
56
|
+
# reflected + transmitted pieces.
|
|
57
|
+
@pytest.mark.slow
|
|
58
|
+
def test_barrier_tunnelling_animates(tmp_path):
|
|
59
|
+
barrier = FiniteSquareWell(V0=-6.0, width=1.0)
|
|
60
|
+
x, frames, times = barrier.wavepacket_scattering(
|
|
61
|
+
x_extent=25.0,
|
|
62
|
+
n_points=1024,
|
|
63
|
+
x0=-10.0,
|
|
64
|
+
sigma0=1.5,
|
|
65
|
+
k0=None,
|
|
66
|
+
dt=1e-3,
|
|
67
|
+
n_steps=5000,
|
|
68
|
+
save_every=500,
|
|
69
|
+
)
|
|
70
|
+
assert frames.shape[0] == len(times) >= 5
|
|
71
|
+
|
|
72
|
+
dx = x[1] - x[0]
|
|
73
|
+
final_density = np.abs(frames[-1]) ** 2
|
|
74
|
+
transmitted = float(np.sum(final_density[x > 0.5]) * dx)
|
|
75
|
+
reflected = float(np.sum(final_density[x < -0.5]) * dx)
|
|
76
|
+
assert transmitted > 0.01 # some probability tunnels through
|
|
77
|
+
assert reflected > 0.01 # some probability reflects
|
|
78
|
+
assert abs((transmitted + reflected) - 1.0) < 0.2
|
|
79
|
+
|
|
80
|
+
anim = animate_density(x, frames, times=times)
|
|
81
|
+
_save_and_check(anim, tmp_path, "barrier_tunnelling.gif")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# 3. Hydrogen orbitals: a two-eigenstate superposition beats in density,
|
|
85
|
+
# unlike either stationary orbital alone.
|
|
86
|
+
def test_hydrogen_orbital_beating_animates():
|
|
87
|
+
orb_a = HydrogenOrbital(2, 0, 0)
|
|
88
|
+
orb_b = HydrogenOrbital(3, 1, 0)
|
|
89
|
+
period = 2 * np.pi / (orb_b.energy - orb_a.energy)
|
|
90
|
+
times = np.linspace(0, abs(period), 4)
|
|
91
|
+
|
|
92
|
+
fig = animate_orbital_beating(orb_a, orb_b, times, n_points=12)
|
|
93
|
+
assert len(fig.frames) == len(times)
|
|
94
|
+
assert fig.data[0].value.size > 0
|
|
95
|
+
assert any(m["buttons"] for m in fig.layout.updatemenus) # a Play button exists
|
|
96
|
+
|
|
97
|
+
r = np.linspace(0.1, 15, 20)
|
|
98
|
+
theta = np.full_like(r, np.pi / 3)
|
|
99
|
+
phi = np.zeros_like(r)
|
|
100
|
+
density_0 = orbital_superposition_density(orb_a, orb_b, r, theta, phi, 0.0)
|
|
101
|
+
density_half = orbital_superposition_density(orb_a, orb_b, r, theta, phi, abs(period) / 2)
|
|
102
|
+
assert np.max(np.abs(density_0 - density_half)) > 1e-6 # genuinely time-dependent
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# 4. Harmonic-oscillator eigenstate superposition: plumbing onto animate_density.
|
|
106
|
+
@pytest.mark.slow
|
|
107
|
+
def test_harmonic_superposition_animates(tmp_path):
|
|
108
|
+
ho = HarmonicOscillator()
|
|
109
|
+
x = np.linspace(-10, 10, 400)
|
|
110
|
+
times = np.linspace(0, 2 * np.pi / ho.omega, 12)
|
|
111
|
+
frames = ho.superposition_trajectory([0, 1, 2], [1.0, 1.0j, 0.5], x, times)
|
|
112
|
+
|
|
113
|
+
for psi in frames:
|
|
114
|
+
assert abs(trapz(np.abs(psi) ** 2, x) - 1.0) < 1e-6
|
|
115
|
+
|
|
116
|
+
anim = animate_density(x, frames, times=times)
|
|
117
|
+
_save_and_check(anim, tmp_path, "harmonic_superposition.gif")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# 5. Free wave-packet dispersion: plumbing onto animate_density.
|
|
121
|
+
@pytest.mark.slow
|
|
122
|
+
def test_wave_packet_dispersion_animates(tmp_path):
|
|
123
|
+
gd = GaussianDispersion(x0=0.0, sigma0=1.0, k0=3.0)
|
|
124
|
+
x = np.linspace(-30, 30, 600)
|
|
125
|
+
times = np.linspace(0, 6.0, 10)
|
|
126
|
+
frames = gd.trajectory(x, times)
|
|
127
|
+
|
|
128
|
+
widths = [gd.width(t) for t in times]
|
|
129
|
+
assert widths[-1] > widths[0] # the packet spreads
|
|
130
|
+
|
|
131
|
+
anim = animate_density(x, frames, times=times)
|
|
132
|
+
_save_and_check(anim, tmp_path, "wave_packet_dispersion.gif")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# 6. Scattering off a finite well: resonance/reflection dynamics, sharing
|
|
136
|
+
# FiniteSquareWell.wavepacket_scattering with the barrier case above.
|
|
137
|
+
@pytest.mark.slow
|
|
138
|
+
def test_well_scattering_animates(tmp_path):
|
|
139
|
+
well = FiniteSquareWell(V0=20.0, width=2.0)
|
|
140
|
+
x, frames, times = well.wavepacket_scattering(
|
|
141
|
+
x_extent=25.0,
|
|
142
|
+
n_points=1024,
|
|
143
|
+
x0=-10.0,
|
|
144
|
+
sigma0=1.5,
|
|
145
|
+
k0=None,
|
|
146
|
+
dt=1e-3,
|
|
147
|
+
n_steps=5000,
|
|
148
|
+
save_every=500,
|
|
149
|
+
)
|
|
150
|
+
assert frames.shape[0] == len(times) >= 5
|
|
151
|
+
|
|
152
|
+
anim = animate_density(x, frames, times=times)
|
|
153
|
+
_save_and_check(anim, tmp_path, "well_scattering.gif")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# 7. Stern-Gerlach: the joint spatial density splits into two lobes.
|
|
157
|
+
@pytest.mark.slow
|
|
158
|
+
def test_stern_gerlach_splitting_animates(tmp_path):
|
|
159
|
+
sg = SternGerlach(mu=2.0, grad_B=1.0, sigma0=1.0, sigma_x=2.0, k0=4.0)
|
|
160
|
+
x = np.linspace(-15, 15, 60)
|
|
161
|
+
y = np.linspace(-15, 15, 80)
|
|
162
|
+
times = np.array([0.0, 1.0, 2.5])
|
|
163
|
+
stack = sg.joint_density_stack(x, y, times)
|
|
164
|
+
assert stack.shape == (len(times), len(x), len(y))
|
|
165
|
+
|
|
166
|
+
def y_spread(density_xy):
|
|
167
|
+
marginal_y = density_xy.sum(axis=0)
|
|
168
|
+
marginal_y /= marginal_y.sum()
|
|
169
|
+
mean_y = np.sum(y * marginal_y)
|
|
170
|
+
return np.sqrt(np.sum((y - mean_y) ** 2 * marginal_y))
|
|
171
|
+
|
|
172
|
+
assert y_spread(stack[-1]) > y_spread(stack[0]) # splits apart over time
|
|
173
|
+
|
|
174
|
+
anim = animate_density_2d(x, y, stack, times=times, phase_colored=False)
|
|
175
|
+
_save_and_check(anim, tmp_path, "stern_gerlach.gif")
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# 8. Rabi oscillations on the Bloch sphere.
|
|
179
|
+
@pytest.mark.slow
|
|
180
|
+
def test_rabi_bloch_sphere_animates(tmp_path):
|
|
181
|
+
rabi = RabiProblem(omega0=1.0, omega_d=1.0, Omega=0.5) # on resonance
|
|
182
|
+
t_pi = np.pi / rabi.Omega # a resonant pi-pulse fully inverts the population
|
|
183
|
+
assert rabi.excited_state_population(np.array([t_pi]))[0] > 0.999
|
|
184
|
+
|
|
185
|
+
times = np.linspace(0, t_pi, 20)
|
|
186
|
+
states = rabi.state_trajectory(times)
|
|
187
|
+
assert np.allclose(np.sum(np.abs(states) ** 2, axis=1), 1.0, atol=1e-10)
|
|
188
|
+
|
|
189
|
+
trajectory = state_to_bloch_trajectory(states)
|
|
190
|
+
assert np.allclose(np.linalg.norm(trajectory, axis=1), 1.0, atol=1e-8)
|
|
191
|
+
|
|
192
|
+
anim = animate_bloch_sphere(trajectory, times=times)
|
|
193
|
+
_save_and_check(anim, tmp_path, "rabi_bloch.gif")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# 9. Entangling Ising coupling: concurrence grows from 0 toward 1.
|
|
197
|
+
@pytest.mark.slow
|
|
198
|
+
def test_entanglement_growth_animates(tmp_path):
|
|
199
|
+
ising = IsingEntangler(J=1.0)
|
|
200
|
+
assert abs(ising.concurrence(ising.initial_state())) < 1e-12 # starts unentangled
|
|
201
|
+
|
|
202
|
+
times = np.linspace(0, np.pi / (2 * ising.J), 30)
|
|
203
|
+
concurrence = ising.concurrence_trajectory(times)
|
|
204
|
+
assert concurrence[0] < 1e-6
|
|
205
|
+
assert concurrence.max() > 0.99 # reaches (near-)maximal entanglement
|
|
206
|
+
|
|
207
|
+
anim = animate_entanglement_growth(times, concurrence)
|
|
208
|
+
_save_and_check(anim, tmp_path, "entanglement_growth.gif")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# 10. Double-well tunneling oscillation: almost pure plumbing onto animate_density.
|
|
212
|
+
@pytest.mark.slow
|
|
213
|
+
def test_double_well_tunneling_animates(tmp_path):
|
|
214
|
+
dw = DoubleWellSimulator(lam=0.3, a=1.5)
|
|
215
|
+
result = dw.solve(n_states=2)
|
|
216
|
+
times = np.linspace(0, result.tunneling_period, 16)
|
|
217
|
+
frames = dw.tunneling_wavefunction(result, times, side="left")
|
|
218
|
+
assert frames.shape == (len(times), len(dw.x))
|
|
219
|
+
|
|
220
|
+
left_prob = dw.left_well_probability(result, times, side="left")
|
|
221
|
+
assert left_prob[0] > 0.9 # starts localized on the left
|
|
222
|
+
assert left_prob[np.argmin(np.abs(times - result.tunneling_period / 2))] < 0.5 # tunnels to the right
|
|
223
|
+
|
|
224
|
+
anim = animate_density(dw.x, frames, times=times)
|
|
225
|
+
_save_and_check(anim, tmp_path, "double_well.gif")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Test for physicskit.quantum._compat's scipy<1.15 fallback branch.
|
|
2
|
+
|
|
3
|
+
The dev/CI environment installs scipy>=1.15 (where scipy.special
|
|
4
|
+
already has sph_harm_y natively), so the ``except ImportError`` branch
|
|
5
|
+
that builds a fallback from the older, deprecated ``sph_harm`` is dead
|
|
6
|
+
code under the installed version -- but it is real code protecting
|
|
7
|
+
users on scipy>=1.10,<1.15 (the package's declared minimum), so it's
|
|
8
|
+
worth exercising directly rather than pragma-excluding it.
|
|
9
|
+
|
|
10
|
+
This simulates the old-scipy environment by temporarily removing
|
|
11
|
+
``scipy.special.sph_harm_y`` and installing a fake ``sph_harm`` with
|
|
12
|
+
the old (m, n, phi, theta) signature/convention, then reloading the
|
|
13
|
+
compat module so it re-runs its import-time fallback logic, and
|
|
14
|
+
restores everything afterward regardless of outcome.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import importlib
|
|
20
|
+
|
|
21
|
+
import scipy.special as sp
|
|
22
|
+
|
|
23
|
+
import physicskit.quantum._compat as compat
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_sph_harm_y_fallback_matches_native_implementation_when_scipy_is_old():
|
|
27
|
+
real_sph_harm_y = sp.sph_harm_y
|
|
28
|
+
|
|
29
|
+
def fake_old_sph_harm(m, n, phi, theta):
|
|
30
|
+
# The old, deprecated scipy.special.sph_harm's argument order and
|
|
31
|
+
# convention: (m, n, azimuthal, polar).
|
|
32
|
+
return real_sph_harm_y(n, m, theta, phi)
|
|
33
|
+
|
|
34
|
+
del sp.sph_harm_y
|
|
35
|
+
sp.sph_harm = fake_old_sph_harm
|
|
36
|
+
try:
|
|
37
|
+
importlib.reload(compat)
|
|
38
|
+
n, m, theta, phi = 2, 1, 0.3, 0.7
|
|
39
|
+
assert compat.sph_harm_y(n, m, theta, phi) == real_sph_harm_y(n, m, theta, phi)
|
|
40
|
+
finally:
|
|
41
|
+
del sp.sph_harm
|
|
42
|
+
sp.sph_harm_y = real_sph_harm_y
|
|
43
|
+
importlib.reload(compat)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Error-path tests for NumerovSolver that test_physics_checks.py's
|
|
2
|
+
accuracy checks never exercise: rejecting a non-uniform grid and
|
|
3
|
+
rejecting a requested state count that isn't smaller than the number
|
|
4
|
+
of interior grid points."""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from physicskit.quantum.core.eigensolvers import NumerovSolver, infinite_well
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_numerov_solver_rejects_non_uniform_grid():
|
|
15
|
+
x = np.array([0.0, 0.1, 0.3, 0.6, 1.0]) # unevenly spaced
|
|
16
|
+
with pytest.raises(ValueError):
|
|
17
|
+
NumerovSolver(x, infinite_well())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_numerov_solver_rejects_n_states_not_smaller_than_interior_points():
|
|
21
|
+
x = np.linspace(0, 1, 6) # 4 interior points
|
|
22
|
+
solver = NumerovSolver(x, infinite_well())
|
|
23
|
+
with pytest.raises(ValueError):
|
|
24
|
+
solver.solve(n_states=4)
|