StochasticForceInference 2.0.2__tar.gz → 2.0.4__tar.gz
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.
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/AGENTS.md +12 -1
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/PKG-INFO +21 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/README.md +19 -2
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/pairs.py +60 -13
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/__init__.py +8 -1
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/assess.py +9 -1
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/dynamics_order.py +66 -70
- stochasticforceinference-2.0.4/SFI/diagnostics/finite_dt.py +183 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/plotting.py +22 -9
- stochasticforceinference-2.0.4/SFI/diagnostics/psd.py +93 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/report.py +82 -7
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/residual_tests.py +57 -18
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/residuals.py +320 -4
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/base.py +712 -70
- stochasticforceinference-2.0.4/SFI/inference/graph_diffusion.py +509 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/overdamped.py +427 -123
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/covariance.py +1 -1
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/driver.py +26 -4
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/flow_multi.py +143 -33
- stochasticforceinference-2.0.4/SFI/inference/parametric_core/frame_extras.py +112 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/precompute.py +58 -26
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/runner.py +365 -81
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/solve.py +103 -22
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/result.py +18 -5
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/result.py +28 -9
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/underdamped.py +569 -177
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/integrate/__init__.py +11 -1
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/integrate/api.py +391 -43
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/integrate/integrand.py +213 -18
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/integrate/timeops.py +23 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/base.py +115 -8
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/chunked.py +2 -3
- stochasticforceinference-2.0.4/SFI/langevin/graph_noise.py +498 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/noise.py +9 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/overdamped.py +43 -23
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/underdamped.py +63 -39
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/factory.py +19 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/base.py +16 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/interactions/__init__.py +2 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/interactions/dispatcher.py +351 -337
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/interactions/specs.py +93 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/leaf.py +56 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/derivative.py +41 -12
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/slice.py +42 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/params.py +25 -9
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/stateexpr.py +27 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/structexpr.py +56 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/dataset.py +95 -55
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/degrade.py +201 -64
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/reserved_extras.py +29 -5
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/utils/formatting.py +32 -4
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/utils/neighbors.py +11 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/utils/plotting.py +245 -51
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/StochasticForceInference.egg-info/PKG-INFO +21 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/StochasticForceInference.egg-info/SOURCES.txt +7 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/StochasticForceInference.egg-info/requires.txt +1 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/abp.py +7 -3
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/coarse_grain.py +26 -16
- stochasticforceinference-2.0.4/examples/_gallery_utils/janus_colloids.py +475 -0
- stochasticforceinference-2.0.4/examples/experimental_data/generate_tweezer_csv.py +53 -0
- stochasticforceinference-2.0.4/examples/gallery/abp_align_demo.py +459 -0
- stochasticforceinference-2.0.4/examples/gallery/abp_nonreciprocal_demo.py +605 -0
- stochasticforceinference-2.0.4/examples/gallery/abp_to_spde_demo.py +831 -0
- stochasticforceinference-2.0.4/examples/gallery/advanced/flocking_3d_demo.py +537 -0
- stochasticforceinference-2.0.4/examples/gallery/advanced/multi_experiment_demo.py +389 -0
- stochasticforceinference-2.0.4/examples/gallery/advanced/nn_force_demo.py +405 -0
- stochasticforceinference-2.0.4/examples/gallery/anisotropic_diffusion_demo.py +380 -0
- stochasticforceinference-2.0.4/examples/gallery/custom_basis_demo.py +329 -0
- stochasticforceinference-2.0.4/examples/gallery/diagnostics_demo.py +303 -0
- stochasticforceinference-2.0.4/examples/gallery/dynamics_order_demo.py +291 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/gallery/entropy_production_demo.py +153 -135
- stochasticforceinference-2.0.4/examples/gallery/entropy_production_underdamped_demo.py +479 -0
- stochasticforceinference-2.0.4/examples/gallery/experimental_workflow_demo.py +415 -0
- stochasticforceinference-2.0.4/examples/gallery/gray_scott_demo.py +715 -0
- stochasticforceinference-2.0.4/examples/gallery/home_range_demo.py +377 -0
- stochasticforceinference-2.0.4/examples/gallery/janus_colloids_demo.py +457 -0
- stochasticforceinference-2.0.4/examples/gallery/limitcycle_demo.py +328 -0
- stochasticforceinference-2.0.4/examples/gallery/lorenz_demo.py +370 -0
- stochasticforceinference-2.0.4/examples/gallery/lotka_volterra_demo.py +595 -0
- stochasticforceinference-2.0.4/examples/gallery/multiplicative_diffusion_demo.py +359 -0
- stochasticforceinference-2.0.4/examples/gallery/ou_demo.py +431 -0
- stochasticforceinference-2.0.4/examples/gallery/time_dependent_forcing_demo.py +269 -0
- stochasticforceinference-2.0.4/examples/gallery/time_fourier_demo.py +341 -0
- stochasticforceinference-2.0.4/examples/gallery/van_der_pol_demo.py +576 -0
- stochasticforceinference-2.0.4/examples/gallery/velocity_dependent_noise_demo.py +373 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/pyproject.toml +2 -2
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/tests/test_parametric_mask_abp.py +1 -0
- stochasticforceinference-2.0.2/examples/experimental_data/generate_tweezer_csv.py +0 -35
- stochasticforceinference-2.0.2/examples/gallery/abp_align_demo.py +0 -574
- stochasticforceinference-2.0.2/examples/gallery/abp_nonreciprocal_demo.py +0 -529
- stochasticforceinference-2.0.2/examples/gallery/abp_to_spde_demo.py +0 -602
- stochasticforceinference-2.0.2/examples/gallery/advanced/flocking_3d_demo.py +0 -314
- stochasticforceinference-2.0.2/examples/gallery/advanced/multi_experiment_demo.py +0 -347
- stochasticforceinference-2.0.2/examples/gallery/advanced/nn_force_demo.py +0 -521
- stochasticforceinference-2.0.2/examples/gallery/anisotropic_diffusion_demo.py +0 -234
- stochasticforceinference-2.0.2/examples/gallery/custom_basis_demo.py +0 -254
- stochasticforceinference-2.0.2/examples/gallery/diagnostics_demo.py +0 -150
- stochasticforceinference-2.0.2/examples/gallery/dynamics_order_demo.py +0 -271
- stochasticforceinference-2.0.2/examples/gallery/entropy_production_underdamped_demo.py +0 -464
- stochasticforceinference-2.0.2/examples/gallery/experimental_workflow_demo.py +0 -216
- stochasticforceinference-2.0.2/examples/gallery/gray_scott_demo.py +0 -574
- stochasticforceinference-2.0.2/examples/gallery/home_range_demo.py +0 -394
- stochasticforceinference-2.0.2/examples/gallery/limitcycle_demo.py +0 -213
- stochasticforceinference-2.0.2/examples/gallery/lorenz_demo.py +0 -219
- stochasticforceinference-2.0.2/examples/gallery/lotka_volterra_demo.py +0 -319
- stochasticforceinference-2.0.2/examples/gallery/multiplicative_diffusion_demo.py +0 -240
- stochasticforceinference-2.0.2/examples/gallery/ou_demo.py +0 -413
- stochasticforceinference-2.0.2/examples/gallery/time_dependent_forcing_demo.py +0 -156
- stochasticforceinference-2.0.2/examples/gallery/time_fourier_demo.py +0 -234
- stochasticforceinference-2.0.2/examples/gallery/van_der_pol_demo.py +0 -245
- stochasticforceinference-2.0.2/examples/gallery/velocity_dependent_noise_demo.py +0 -202
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/CONTRIBUTING.md +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/GALLERY_STYLE_GUIDE.md +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/LICENSE +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/MANIFEST.in +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/constants.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/linear.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/monomials.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/bases/spde.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/optimizers.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/banded.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/flow.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/flow_ud.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/gram.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/parametric_core/jacobians.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/serialization.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/base.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/beam.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/greedy.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/hillclimb.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/lasso.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/metrics.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/scorer.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparse/stlsq.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/inference/sparsity.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/integrate/rk4.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/langevin/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/basis.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/core/runtime.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/interactor.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/_base.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/_eval_compiler.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/_fd_atoms.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/_grid.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/layout/_sectors.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/memhint.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/contract.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/interactions/prepare.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/interactions/stencils.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/concat.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/einsum.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/linear.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/mapn.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/nodes/ops/reshape_rank.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/psf.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/statefunc/sf.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/collection.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/trajectory/io.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/utils/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/utils/maths.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/StochasticForceInference.egg-info/dependency_links.txt +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/StochasticForceInference.egg-info/top_level.txt +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/__init__.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/bench_utils.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/helpers.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/_gallery_utils/scenario_picker.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/experimental_data/_analyze_results.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/experimental_data/_expanded_basis.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_align_demo.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_largescale_config.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_largescale_demo.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_largescale_infer.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_largescale_plot.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_largescale_simulate.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/examples/manual_examples/abp_pursuit_demo.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/setup.cfg +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/tests/test_dual_mask.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/tests/test_parametric_mask.py +0 -0
- {stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/tests/test_same_particle_jacobian.py +0 -0
|
@@ -45,6 +45,7 @@ SFI has mature, well-tested abstractions for every common operation.
|
|
|
45
45
|
| Wrap a user function as a state function | `SFI.statefunc.make_basis` / `make_sf` / `make_psf` |
|
|
46
46
|
| Run force / diffusion inference | `SFI.OverdampedLangevinInference` / `UnderdampedLangevinInference` |
|
|
47
47
|
| Select a sparse model | `inf.sparsify_force(criterion="PASTIS", p=0.1)` |
|
|
48
|
+
| Select a sparse diffusion model | `inf.sparsify_diffusion(criterion="PASTIS", p=0.05)` |
|
|
48
49
|
| Compare to ground truth | `inf.compare_to_exact(model_exact=...)` |
|
|
49
50
|
| Plot in the SFI style | `SFI.utils.plotting.SFI_COLORS`, `dark_fig`, `phase2d` |
|
|
50
51
|
| Save / load a fitted model | `SFI.inference.save_model` / `load_model` |
|
|
@@ -78,6 +79,16 @@ from SFI import (
|
|
|
78
79
|
The dynamics-order classifier (`classify_dynamics`, returning a
|
|
79
80
|
`DynamicsOrderReport`) decides overdamped vs underdamped from raw
|
|
80
81
|
positions; see `SFI.diagnostics` and `examples/gallery/dynamics_order_demo.py`.
|
|
82
|
+
`SFI.diagnostics.finite_dt_scale(inf)` reports the per-step drift-gradient
|
|
83
|
+
scale Δt‖∇F̂‖ of a fitted force (the size of the linear estimators' O(Δt)
|
|
84
|
+
biases) with a `fine` / `coarse` / `unreliable` verdict; `assess(inf)` includes
|
|
85
|
+
it and flags the coarse regimes. Both linear engines also record the force
|
|
86
|
+
Gram condition number (`metadata["force_gram_cond"]`) and warn when the
|
|
87
|
+
dictionary is ill-conditioned on the data.
|
|
88
|
+
`SFI.diagnostics.psd_fraction(D_sf, coll)` reports the fraction of data
|
|
89
|
+
points where a diffusion field is not positive semi-definite;
|
|
90
|
+
`simulate_bootstrapped_trajectory` runs it on the fitted diffusion and warns
|
|
91
|
+
(the simulator clips negative eigenvalues silently).
|
|
81
92
|
|
|
82
93
|
### 4.2 `SFI.statefunc` — composable state functions
|
|
83
94
|
|
|
@@ -89,7 +100,7 @@ positions; see `SFI.diagnostics` and `examples/gallery/dynamics_order_demo.py`.
|
|
|
89
100
|
| `make_sf(func, dim, rank, ...)` | Wrap a Python callable as an `SF` |
|
|
90
101
|
| `make_psf(func, dim, rank, ...)` | Wrap a callable with parameters as `PSF` |
|
|
91
102
|
| `make_basis(func, dim, ...)` | Wrap a vector-valued callable as `Basis` |
|
|
92
|
-
| `Interactor`, `make_interactor` | Multi-scale interaction graphs
|
|
103
|
+
| `Interactor`, `make_interactor` | Multi-scale interaction graphs (pairs, hyperedges; `per_slot=True` for many-body forces) |
|
|
93
104
|
|
|
94
105
|
### 4.3 `SFI.inference` — inference engines
|
|
95
106
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: StochasticForceInference
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Stochastic Force Inference for Langevin SDEs
|
|
5
5
|
Author-email: Pierre Ronceray <pierre.ronceray@univ-amu.fr>
|
|
6
6
|
License: MIT
|
|
@@ -42,6 +42,7 @@ Requires-Dist: sphinxcontrib-video; extra == "docs"
|
|
|
42
42
|
Requires-Dist: sphinxcontrib-mermaid; extra == "docs"
|
|
43
43
|
Provides-Extra: dev
|
|
44
44
|
Requires-Dist: pytest; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-xdist; extra == "dev"
|
|
45
46
|
Requires-Dist: ruff; extra == "dev"
|
|
46
47
|
Requires-Dist: pyright; extra == "dev"
|
|
47
48
|
Requires-Dist: StochasticForceInference[docs]; extra == "dev"
|
|
@@ -57,6 +58,7 @@ Dynamic: license-file
|
|
|
57
58
|
[](https://stochasticforceinference.readthedocs.io/en/latest/)
|
|
58
59
|
[](https://pypi.org/project/StochasticForceInference/)
|
|
59
60
|
[](https://github.com/ronceray/StochasticForceInference/blob/main/LICENSE)
|
|
61
|
+
[](https://doi.org/10.5281/zenodo.21532842)
|
|
60
62
|
|
|
61
63
|
**Infer force and diffusion fields from stochastic trajectory data.**
|
|
62
64
|
|
|
@@ -197,11 +199,27 @@ guidance.
|
|
|
197
199
|
## Citation
|
|
198
200
|
|
|
199
201
|
If you use SFI, please cite the software (see [`CITATION.cff`](CITATION.cff))
|
|
200
|
-
together with the method paper(s) relevant to the features you use.
|
|
201
|
-
|
|
202
|
+
together with the method paper(s) relevant to the features you use.
|
|
203
|
+
|
|
204
|
+
The software is archived on Zenodo. Cite the concept DOI
|
|
205
|
+
[10.5281/zenodo.21532842](https://doi.org/10.5281/zenodo.21532842), which always
|
|
206
|
+
resolves to the latest release; each version also has its own DOI (v2.0.3:
|
|
207
|
+
[10.5281/zenodo.21532843](https://doi.org/10.5281/zenodo.21532843)).
|
|
208
|
+
|
|
209
|
+
See **[How to cite](https://stochasticforceinference.readthedocs.io/en/latest/#how-to-cite)** in the
|
|
202
210
|
documentation for the full, feature-specific list (overdamped, underdamped/ULI,
|
|
203
211
|
trapeze, and PASTIS references).
|
|
204
212
|
|
|
213
|
+
## Funding
|
|
214
|
+
|
|
215
|
+
Funded by the European Union (ERC, SuperStoc, 101117322). Views and opinions
|
|
216
|
+
expressed are however those of the author(s) only and do not necessarily
|
|
217
|
+
reflect those of the European Union or the European Research Council Executive
|
|
218
|
+
Agency. Neither the European Union nor the granting authority can be held
|
|
219
|
+
responsible for them. P. R. also acknowledges funding from the French National
|
|
220
|
+
Research Agency (ANR-16-CONV-0001) and from the Excellence Initiative of
|
|
221
|
+
Aix-Marseille University A\*MIDEX.
|
|
222
|
+
|
|
205
223
|
## License
|
|
206
224
|
|
|
207
225
|
MIT — see [LICENSE](https://github.com/ronceray/StochasticForceInference/blob/main/LICENSE) for details.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://stochasticforceinference.readthedocs.io/en/latest/)
|
|
4
4
|
[](https://pypi.org/project/StochasticForceInference/)
|
|
5
5
|
[](https://github.com/ronceray/StochasticForceInference/blob/main/LICENSE)
|
|
6
|
+
[](https://doi.org/10.5281/zenodo.21532842)
|
|
6
7
|
|
|
7
8
|
**Infer force and diffusion fields from stochastic trajectory data.**
|
|
8
9
|
|
|
@@ -143,11 +144,27 @@ guidance.
|
|
|
143
144
|
## Citation
|
|
144
145
|
|
|
145
146
|
If you use SFI, please cite the software (see [`CITATION.cff`](CITATION.cff))
|
|
146
|
-
together with the method paper(s) relevant to the features you use.
|
|
147
|
-
|
|
147
|
+
together with the method paper(s) relevant to the features you use.
|
|
148
|
+
|
|
149
|
+
The software is archived on Zenodo. Cite the concept DOI
|
|
150
|
+
[10.5281/zenodo.21532842](https://doi.org/10.5281/zenodo.21532842), which always
|
|
151
|
+
resolves to the latest release; each version also has its own DOI (v2.0.3:
|
|
152
|
+
[10.5281/zenodo.21532843](https://doi.org/10.5281/zenodo.21532843)).
|
|
153
|
+
|
|
154
|
+
See **[How to cite](https://stochasticforceinference.readthedocs.io/en/latest/#how-to-cite)** in the
|
|
148
155
|
documentation for the full, feature-specific list (overdamped, underdamped/ULI,
|
|
149
156
|
trapeze, and PASTIS references).
|
|
150
157
|
|
|
158
|
+
## Funding
|
|
159
|
+
|
|
160
|
+
Funded by the European Union (ERC, SuperStoc, 101117322). Views and opinions
|
|
161
|
+
expressed are however those of the author(s) only and do not necessarily
|
|
162
|
+
reflect those of the European Union or the European Research Council Executive
|
|
163
|
+
Agency. Neither the European Union nor the granting authority can be held
|
|
164
|
+
responsible for them. P. R. also acknowledges funding from the French National
|
|
165
|
+
Research Agency (ANR-16-CONV-0001) and from the Excellence Initiative of
|
|
166
|
+
Aix-Marseille University A\*MIDEX.
|
|
167
|
+
|
|
151
168
|
## License
|
|
152
169
|
|
|
153
170
|
MIT — see [LICENSE](https://github.com/ronceray/StochasticForceInference/blob/main/LICENSE) for details.
|
|
@@ -29,8 +29,8 @@ from ..statefunc import Basis, Interactor, make_basis, make_interactor
|
|
|
29
29
|
# ═══════════════════════════════════════════════════════════════════════
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
def pbc_displacement(xj, xi, box):
|
|
33
|
-
"""Minimum-image displacement ``xj - xi`` under periodic boundaries.
|
|
32
|
+
def pbc_displacement(xj, xi, box, ref=None):
|
|
33
|
+
r"""Minimum-image displacement ``xj - xi`` under periodic boundaries.
|
|
34
34
|
|
|
35
35
|
Works in any dimension. All inputs are plain JAX arrays of shape
|
|
36
36
|
``(d,)`` (or broadcastable).
|
|
@@ -41,14 +41,49 @@ def pbc_displacement(xj, xi, box):
|
|
|
41
41
|
Positions (or sub-positions) of two particles.
|
|
42
42
|
box : array, shape ``(d,)``
|
|
43
43
|
Box lengths along each axis.
|
|
44
|
+
ref : tuple ``(ref_j, ref_i)`` of arrays, optional
|
|
45
|
+
Reference positions of the same two particles (same axes as
|
|
46
|
+
``xj``, ``xi``). The periodic image is the minimum image of
|
|
47
|
+
``ref_j - ref_i`` instead of ``xj - xi``. Re-picking the image at
|
|
48
|
+
every frame makes direction-carrying features such as
|
|
49
|
+
:math:`\phi(r)\,\hat r_{ij}` jump when a pair crosses the cut
|
|
50
|
+
``|Δx| = L/2``, and a program combining a feature at several
|
|
51
|
+
frames (Stratonovich trapezoid, shifted Gram, ULI) integrates the
|
|
52
|
+
jumps into an :math:`O(1)` bias. The integrator therefore hands
|
|
53
|
+
such programs one reference frame per row (a frame preceding every
|
|
54
|
+
increment of the row, see
|
|
55
|
+
:meth:`~SFI.integrate.Integrand.pbc_reference_in`) as the reserved
|
|
56
|
+
per-particle extra ``extras["pbc_ref"]``. Hand-written pair
|
|
57
|
+
interactors declare ``particle_extras=("pbc_ref",)``, receive it
|
|
58
|
+
with shape ``(K, d)`` and pass ``ref=(pr[1], pr[0])`` (sliced to
|
|
59
|
+
the spatial axes) when present.
|
|
44
60
|
|
|
45
61
|
Returns
|
|
46
62
|
-------
|
|
47
63
|
dx : array, shape ``(d,)``
|
|
48
|
-
``xj - xi`` folded via minimum-image convention
|
|
64
|
+
``xj - xi`` folded via minimum-image convention (of ``ref``
|
|
65
|
+
when given).
|
|
49
66
|
"""
|
|
50
67
|
dx = xj - xi
|
|
51
|
-
|
|
68
|
+
d_img = dx if ref is None else ref[0] - ref[1]
|
|
69
|
+
return dx - box * jnp.round(d_img / box)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Reserved per-particle extra: reference positions fixing the periodic image
|
|
73
|
+
# of every pair (see :func:`pbc_displacement`); SFI.integrate.integrand.PBC_REF_KEY.
|
|
74
|
+
_PBC_REF_KEY = "pbc_ref"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _pbc_ref(extras):
|
|
78
|
+
"""The ``pbc_ref`` reference positions ``(2, d)`` of a pair, or None."""
|
|
79
|
+
if not extras:
|
|
80
|
+
return None
|
|
81
|
+
return extras.get(_PBC_REF_KEY)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _pbc_particle_extras(box):
|
|
85
|
+
"""Periodic pair primitives gather the optional image reference per pair."""
|
|
86
|
+
return (_PBC_REF_KEY,) if box is not None else ()
|
|
52
87
|
|
|
53
88
|
|
|
54
89
|
def wrap_angle(a):
|
|
@@ -56,7 +91,7 @@ def wrap_angle(a):
|
|
|
56
91
|
return (a + jnp.pi) % (2.0 * jnp.pi) - jnp.pi
|
|
57
92
|
|
|
58
93
|
|
|
59
|
-
def _pairwise_dr(XK, *, box=None, spatial_dims=None, eps=1e-12):
|
|
94
|
+
def _pairwise_dr(XK, *, box=None, spatial_dims=None, eps=1e-12, ref=None):
|
|
60
95
|
"""Compute displacement, distance and unit vector for a K=2 pair.
|
|
61
96
|
|
|
62
97
|
Parameters
|
|
@@ -70,6 +105,9 @@ def _pairwise_dr(XK, *, box=None, spatial_dims=None, eps=1e-12):
|
|
|
70
105
|
Default: all.
|
|
71
106
|
eps : float
|
|
72
107
|
Regularisation to avoid division by zero.
|
|
108
|
+
ref : array, shape ``(2, dim)``, optional
|
|
109
|
+
Reference states of the pair fixing the periodic image (see
|
|
110
|
+
:func:`pbc_displacement`).
|
|
73
111
|
|
|
74
112
|
Returns
|
|
75
113
|
-------
|
|
@@ -83,7 +121,9 @@ def _pairwise_dr(XK, *, box=None, spatial_dims=None, eps=1e-12):
|
|
|
83
121
|
else:
|
|
84
122
|
xi_s, xj_s = xi, xj
|
|
85
123
|
if box is not None:
|
|
86
|
-
|
|
124
|
+
if ref is not None:
|
|
125
|
+
ref = (ref[1], ref[0]) if spatial_dims is None else (ref[1][spatial_dims], ref[0][spatial_dims])
|
|
126
|
+
dx = pbc_displacement(xj_s, xi_s, box, ref=ref)
|
|
87
127
|
else:
|
|
88
128
|
dx = xj_s - xi_s
|
|
89
129
|
r = jnp.sqrt(jnp.sum(dx**2) + eps)
|
|
@@ -303,7 +343,7 @@ def radial_pair_basis(
|
|
|
303
343
|
b = extras["box"]
|
|
304
344
|
if spatial_dims is not None:
|
|
305
345
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
306
|
-
dx, r, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
346
|
+
dx, r, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
307
347
|
|
|
308
348
|
# Evaluate kernels → (n_feat,)
|
|
309
349
|
vals = jnp.stack([fn(r) for fn in fns]) # (F,)
|
|
@@ -331,6 +371,7 @@ def radial_pair_basis(
|
|
|
331
371
|
K=2,
|
|
332
372
|
n_features=n_feat,
|
|
333
373
|
extras_keys=extras_keys,
|
|
374
|
+
particle_extras=_pbc_particle_extras(box),
|
|
334
375
|
labels=list(labels),
|
|
335
376
|
descriptor="radial-pair-basis",
|
|
336
377
|
)
|
|
@@ -377,7 +418,7 @@ def scalar_pair_basis(
|
|
|
377
418
|
b = extras["box"]
|
|
378
419
|
if spatial_dims is not None:
|
|
379
420
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
380
|
-
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
421
|
+
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
381
422
|
return jnp.stack([fn(r) for fn in fns]) # (F,)
|
|
382
423
|
|
|
383
424
|
extras_keys = ("box",) if _use_extras_box else ()
|
|
@@ -388,6 +429,7 @@ def scalar_pair_basis(
|
|
|
388
429
|
K=2,
|
|
389
430
|
n_features=n_feat,
|
|
390
431
|
extras_keys=extras_keys,
|
|
432
|
+
particle_extras=_pbc_particle_extras(box),
|
|
391
433
|
labels=list(labels),
|
|
392
434
|
descriptor="scalar-pair-basis",
|
|
393
435
|
)
|
|
@@ -460,7 +502,7 @@ def angular_pair_basis(
|
|
|
460
502
|
b = extras["box"]
|
|
461
503
|
if spatial_dims is not None:
|
|
462
504
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
463
|
-
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
505
|
+
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
464
506
|
# angle coupling
|
|
465
507
|
dth = wrap_angle(xj[angle_index] - xi[angle_index])
|
|
466
508
|
g = coupling_fn(dth)
|
|
@@ -478,6 +520,7 @@ def angular_pair_basis(
|
|
|
478
520
|
K=2,
|
|
479
521
|
n_features=n_feat,
|
|
480
522
|
extras_keys=extras_keys,
|
|
523
|
+
particle_extras=_pbc_particle_extras(box),
|
|
481
524
|
labels=list(labels),
|
|
482
525
|
descriptor="angular-pair-basis",
|
|
483
526
|
)
|
|
@@ -575,7 +618,7 @@ def dyadic_pair_basis(
|
|
|
575
618
|
b = extras["box"]
|
|
576
619
|
if spatial_dims is not None:
|
|
577
620
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
578
|
-
_, r, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
621
|
+
_, r, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
579
622
|
vals = jnp.stack([fn(r) for fn in fns]) # (F,)
|
|
580
623
|
# rhat ⊗ rhat: (d, d) then weight by each kernel → (d, d, F)
|
|
581
624
|
rr = jnp.outer(rhat, rhat) # (d, d)
|
|
@@ -589,6 +632,7 @@ def dyadic_pair_basis(
|
|
|
589
632
|
K=2,
|
|
590
633
|
n_features=n_feat,
|
|
591
634
|
extras_keys=extras_keys,
|
|
635
|
+
particle_extras=_pbc_particle_extras(box),
|
|
592
636
|
labels=list(labels),
|
|
593
637
|
descriptor="dyadic-pair-basis",
|
|
594
638
|
)
|
|
@@ -656,7 +700,7 @@ def pair_direction(
|
|
|
656
700
|
b = extras["box"]
|
|
657
701
|
if spatial_dims is not None:
|
|
658
702
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
659
|
-
_, _, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
703
|
+
_, _, rhat = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
660
704
|
if embed_dim is not None:
|
|
661
705
|
full = jnp.zeros(out_d, dtype=XK.dtype)
|
|
662
706
|
for k, ax in enumerate(embed_axes):
|
|
@@ -672,6 +716,7 @@ def pair_direction(
|
|
|
672
716
|
K=2,
|
|
673
717
|
n_features=1,
|
|
674
718
|
extras_keys=extras_keys,
|
|
719
|
+
particle_extras=_pbc_particle_extras(box),
|
|
675
720
|
labels=("r̂_ij",),
|
|
676
721
|
descriptor="pair-direction",
|
|
677
722
|
)
|
|
@@ -828,7 +873,7 @@ def vision_gate(
|
|
|
828
873
|
b = extras["box"]
|
|
829
874
|
if spatial_dims is not None:
|
|
830
875
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
831
|
-
dx, _, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
876
|
+
dx, _, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
832
877
|
if dx.shape[0] != 2:
|
|
833
878
|
raise ValueError(
|
|
834
879
|
f"vision_gate requires exactly 2 spatial dimensions, got {dx.shape[0]}. "
|
|
@@ -848,6 +893,7 @@ def vision_gate(
|
|
|
848
893
|
K=2,
|
|
849
894
|
n_features=1,
|
|
850
895
|
extras_keys=extras_keys,
|
|
896
|
+
particle_extras=_pbc_particle_extras(box),
|
|
851
897
|
labels=("vision",),
|
|
852
898
|
descriptor="vision-gate",
|
|
853
899
|
)
|
|
@@ -895,7 +941,7 @@ def parametric_radial_kernel(
|
|
|
895
941
|
b = extras["box"]
|
|
896
942
|
if spatial_dims is not None:
|
|
897
943
|
b = b[spatial_dims] if b.ndim > 0 else b
|
|
898
|
-
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims)
|
|
944
|
+
_, r, _ = _pairwise_dr(XK, box=b, spatial_dims=spatial_dims, ref=_pbc_ref(extras))
|
|
899
945
|
return kernel_fn(r, params)[None] # (1,) — rank-0, 1 feature
|
|
900
946
|
|
|
901
947
|
extras_keys = ("box",) if _use_extras_box else ()
|
|
@@ -907,6 +953,7 @@ def parametric_radial_kernel(
|
|
|
907
953
|
n_features=1,
|
|
908
954
|
params=params,
|
|
909
955
|
extras_keys=extras_keys,
|
|
956
|
+
particle_extras=_pbc_particle_extras(box),
|
|
910
957
|
labels=("k(r)",),
|
|
911
958
|
descriptor="parametric-radial-kernel",
|
|
912
959
|
)
|
{stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/__init__.py
RENAMED
|
@@ -15,7 +15,10 @@ like an independent ``N(0, 1)`` sample:
|
|
|
15
15
|
* Ljung--Box autocorrelation of the residuals and their squares;
|
|
16
16
|
* Kolmogorov--Smirnov normality against ``N(0, 1)``;
|
|
17
17
|
* predicted-vs-realised normalised mean squared error of the force
|
|
18
|
-
(a sampling-noise-aware chi-square check)
|
|
18
|
+
(a sampling-noise-aware chi-square check);
|
|
19
|
+
* the finite sampling-interval scale ``dt*|grad F|`` of the fitted force
|
|
20
|
+
(:func:`finite_dt_scale`), the size of the O(dt) biases of the linear
|
|
21
|
+
estimators.
|
|
19
22
|
|
|
20
23
|
See :mod:`SFI.diagnostics.residual_tests`. Two preset levels are exposed:
|
|
21
24
|
|
|
@@ -36,6 +39,8 @@ Ljung & Box (1978); Diebold, Gunther & Tay (1998).
|
|
|
36
39
|
|
|
37
40
|
from .assess import assess
|
|
38
41
|
from .dynamics_order import DynamicsOrderReport, classify_dynamics
|
|
42
|
+
from .finite_dt import finite_dt_scale
|
|
43
|
+
from .psd import psd_fraction
|
|
39
44
|
from .plotting import (
|
|
40
45
|
plot_dynamics_order,
|
|
41
46
|
plot_qq,
|
|
@@ -51,10 +56,12 @@ __all__ = [
|
|
|
51
56
|
"classify_dynamics",
|
|
52
57
|
"DiagnosticsReport",
|
|
53
58
|
"DynamicsOrderReport",
|
|
59
|
+
"finite_dt_scale",
|
|
54
60
|
"parametric_four_point_diagnostic",
|
|
55
61
|
"plot_dynamics_order",
|
|
56
62
|
"plot_qq",
|
|
57
63
|
"plot_residual_acf",
|
|
58
64
|
"plot_residual_histogram",
|
|
59
65
|
"plot_summary",
|
|
66
|
+
"psd_fraction",
|
|
60
67
|
]
|
|
@@ -8,6 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
from typing import Optional
|
|
9
9
|
|
|
10
10
|
from .report import DiagnosticsReport
|
|
11
|
+
from .finite_dt import finite_dt_scale
|
|
11
12
|
from .residual_tests import (
|
|
12
13
|
autocorrelation_tests,
|
|
13
14
|
mse_consistency,
|
|
@@ -74,6 +75,13 @@ def assess(
|
|
|
74
75
|
residuals["normality"] = normality_test(bundle)
|
|
75
76
|
residuals["mse_consistency"] = mse_consistency(inferer, bundle)
|
|
76
77
|
|
|
78
|
+
finite_dt: dict = {}
|
|
79
|
+
if level == "standard":
|
|
80
|
+
try:
|
|
81
|
+
finite_dt = finite_dt_scale(inferer)
|
|
82
|
+
except Exception as exc: # never let the guard break the residual report
|
|
83
|
+
finite_dt = {"available": False, "reason": f"{type(exc).__name__}: {exc}", "verdict": "unavailable"}
|
|
84
|
+
|
|
77
85
|
meta = {
|
|
78
86
|
"backend": bundle.backend,
|
|
79
87
|
"regime": bundle.regime,
|
|
@@ -84,4 +92,4 @@ def assess(
|
|
|
84
92
|
"inferer_class": type(inferer).__name__,
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
return DiagnosticsReport(residuals=residuals, meta=meta)
|
|
95
|
+
return DiagnosticsReport(residuals=residuals, meta=meta, finite_dt=finite_dt)
|
{stochasticforceinference-2.0.2 → stochasticforceinference-2.0.4}/SFI/diagnostics/dynamics_order.py
RENAMED
|
@@ -29,71 +29,70 @@ cross-check (:func:`_cross_check`) into an OD / UD / inconclusive verdict.
|
|
|
29
29
|
from __future__ import annotations
|
|
30
30
|
|
|
31
31
|
import json
|
|
32
|
+
import warnings
|
|
32
33
|
from dataclasses import dataclass, field
|
|
33
34
|
from typing import Optional
|
|
34
35
|
|
|
35
|
-
import jax.numpy as jnp
|
|
36
36
|
import numpy as np
|
|
37
37
|
from scipy.optimize import least_squares
|
|
38
38
|
|
|
39
39
|
from SFI.diagnostics.report import _to_jsonable
|
|
40
|
-
from SFI.integrate import Integrand, Term, TimeOperand, integrate, timeop
|
|
41
40
|
|
|
42
41
|
# --------------------------------------------------------------------------- #
|
|
43
|
-
# Lag-0/1/2 displacement
|
|
42
|
+
# Lag-0/1/2 displacement covariances.
|
|
44
43
|
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
# lag-2 covariance (displacements one step apart).
|
|
44
|
+
# Plain moment sums over the increments dX[t] = X[t+1] - X[t], with the same
|
|
45
|
+
# row semantics as ``integrate(..., reduce="mean")`` on the dataset streams
|
|
46
|
+
# (dX_minus = dX[t-1], dX_plus = dX[t+1]): a row (t, i) counts when the
|
|
47
|
+
# dynamic mask holds at t and the static mask at every other frame its
|
|
48
|
+
# increments touch; rows are weighted by dt and the dataset weight. Computed
|
|
49
|
+
# in NumPy: each coarse-graining stride has its own length, and a JAX path
|
|
50
|
+
# recompiles for every one of them.
|
|
53
51
|
# --------------------------------------------------------------------------- #
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
""
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
53
|
+
# rows t in [first, T - tail), increments (dX[t+a], dX[t+b]), static-mask frames t+off
|
|
54
|
+
_LAGS = {
|
|
55
|
+
"C0": (0, 1, (0, 0), (1,)),
|
|
56
|
+
"C1": (1, 1, (0, -1), (-1, 1)),
|
|
57
|
+
"C2": (1, 2, (-1, 1), (-1, 1, 2)),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _dataset_steps(ds, T):
|
|
62
|
+
"""Per-interval steps ``(T-1,)`` of a dataset (``t[k+1] - t[k]``)."""
|
|
63
|
+
if ds.t is not None:
|
|
64
|
+
return np.diff(np.asarray(ds.t, dtype=float))
|
|
65
|
+
if ds.dt is None:
|
|
66
|
+
raise ValueError("Both 't' and 'dt' are None; one must be provided.")
|
|
67
|
+
dt = np.asarray(ds.dt, dtype=float)
|
|
68
|
+
return np.broadcast_to(dt, (T - 1,)) if dt.ndim == 0 else dt[: T - 1]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _lag_covariances(coll):
|
|
72
|
+
"""Time- and particle-averaged ``(d, d)`` lag-0/1/2 covariances of ``coll``."""
|
|
73
|
+
acc = dict.fromkeys(_LAGS, 0.0)
|
|
74
|
+
norm = dict.fromkeys(_LAGS, 0.0)
|
|
75
|
+
for ds, w in zip(coll.datasets, np.asarray(coll.weights, dtype=float)):
|
|
76
|
+
X = np.asarray(ds._X3d(), dtype=float) # (T, N, d)
|
|
77
|
+
M = np.asarray(ds._M2d()) # static mask (T, N)
|
|
78
|
+
Mdyn = np.asarray(ds._dynamic_M2d())
|
|
79
|
+
T = X.shape[0]
|
|
80
|
+
steps = _dataset_steps(ds, T)
|
|
81
|
+
dX = np.diff(X, axis=0)
|
|
82
|
+
for key, (first, tail, (a, b), frames) in _LAGS.items():
|
|
83
|
+
t = np.arange(first, T - tail)
|
|
84
|
+
if t.size == 0:
|
|
85
|
+
continue
|
|
86
|
+
m = Mdyn[t].copy()
|
|
87
|
+
for off in frames:
|
|
88
|
+
m &= M[t + off]
|
|
89
|
+
u, v = dX[t + a], dX[t + b]
|
|
90
|
+
# NaN-safe masking: masked frames may hold NaN placeholders.
|
|
91
|
+
uv = np.einsum("kim,kin->kimn", u, v)
|
|
92
|
+
uv = np.where(m[..., None, None], 0.5 * (uv + np.swapaxes(uv, -1, -2)), 0.0)
|
|
93
|
+
acc[key] = acc[key] + w * np.einsum("k,kimn->mn", steps[t], uv)
|
|
94
|
+
norm[key] = norm[key] + w * float(np.sum(steps[t] * m.sum(axis=1)))
|
|
95
|
+
return {k: acc[k] / norm[k] if norm[k] > 0 else np.asarray(0.0) for k in _LAGS}
|
|
97
96
|
|
|
98
97
|
|
|
99
98
|
def _mean_dt(coll):
|
|
@@ -112,13 +111,9 @@ def _mean_dt(coll):
|
|
|
112
111
|
return float(np.mean(dts))
|
|
113
112
|
|
|
114
113
|
|
|
115
|
-
def _n_eff(coll
|
|
116
|
-
"""Number of
|
|
117
|
-
|
|
118
|
-
for ds in coll.datasets:
|
|
119
|
-
idx = ds.valid_indices(frozenset(require))
|
|
120
|
-
n += int(np.asarray(idx).size) * int(ds.N)
|
|
121
|
-
return n
|
|
114
|
+
def _n_eff(coll):
|
|
115
|
+
"""Number of lag-2 increment pairs (rows ``t = 1 .. T-3``, all particles)."""
|
|
116
|
+
return sum(max(int(ds.T) - 3, 0) * int(ds.N) for ds in coll.datasets)
|
|
122
117
|
|
|
123
118
|
|
|
124
119
|
def _increment_covariances(coll):
|
|
@@ -135,15 +130,13 @@ def _increment_covariances(coll):
|
|
|
135
130
|
``C0``, ``C1``, ``C2`` : ``(d, d)`` displacement covariance tensors;
|
|
136
131
|
``dt`` : representative scalar step; ``n_eff`` : valid sample count.
|
|
137
132
|
"""
|
|
138
|
-
|
|
139
|
-
C1 = _integrate_tensor(coll, _cov_lag1, "c1")
|
|
140
|
-
C2 = _integrate_tensor(coll, _cov_lag2, "c2")
|
|
133
|
+
cov = _lag_covariances(coll)
|
|
141
134
|
return {
|
|
142
|
-
"C0": C0,
|
|
143
|
-
"C1": C1,
|
|
144
|
-
"C2": C2,
|
|
135
|
+
"C0": cov["C0"],
|
|
136
|
+
"C1": cov["C1"],
|
|
137
|
+
"C2": cov["C2"],
|
|
145
138
|
"dt": _mean_dt(coll),
|
|
146
|
-
"n_eff": _n_eff(coll
|
|
139
|
+
"n_eff": _n_eff(coll),
|
|
147
140
|
}
|
|
148
141
|
|
|
149
142
|
|
|
@@ -432,7 +425,6 @@ def _cross_check(data, *, force_order: int = 3):
|
|
|
432
425
|
quick fit fails for any reason.
|
|
433
426
|
"""
|
|
434
427
|
try:
|
|
435
|
-
from SFI.bases.constants import unit_vector_basis
|
|
436
428
|
from SFI.bases.monomials import monomials_up_to
|
|
437
429
|
from SFI.diagnostics.assess import assess
|
|
438
430
|
from SFI.inference.overdamped import OverdampedLangevinInference
|
|
@@ -441,8 +433,12 @@ def _cross_check(data, *, force_order: int = 3):
|
|
|
441
433
|
inf = OverdampedLangevinInference(data)
|
|
442
434
|
inf.compute_diffusion_constant(method="auto")
|
|
443
435
|
basis = monomials_up_to(order=force_order, dim=d, include_constant=True, include_x=True, include_v=False)
|
|
444
|
-
|
|
445
|
-
|
|
436
|
+
with warnings.catch_warnings():
|
|
437
|
+
# A corroboration fit on a deliberately misfit model: its Gram
|
|
438
|
+
# conditioning is not the user's concern.
|
|
439
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
440
|
+
inf.infer_force_linear(basis.vectorize(d))
|
|
441
|
+
inf.compute_force_error()
|
|
446
442
|
report = assess(inf, level="standard")
|
|
447
443
|
lb = report.residuals.get("autocorr", {}).get("ljung_box", {})
|
|
448
444
|
return {"ljung_box_pvalue": lb.get("pvalue"), "ljung_box_stat": lb.get("statistic")}
|