pystatistics 2.0.1__tar.gz → 2.1.0__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.
- {pystatistics-2.0.1 → pystatistics-2.1.0}/CHANGELOG.md +213 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/PKG-INFO +103 -1
- {pystatistics-2.0.1 → pystatistics-2.1.0}/README.md +102 -0
- pystatistics-2.1.0/benchmarks/mvnmle_bench.py +194 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pyproject.toml +1 -1
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/__init__.py +1 -1
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/__init__.py +14 -1
- pystatistics-2.1.0/pystatistics/mvnmle/_monotone.py +230 -0
- pystatistics-2.1.0/pystatistics/mvnmle/backends/_em_batched.py +795 -0
- pystatistics-2.1.0/pystatistics/mvnmle/backends/_squarem.py +235 -0
- pystatistics-2.1.0/pystatistics/mvnmle/backends/em.py +469 -0
- pystatistics-2.1.0/pystatistics/mvnmle/mcar_test.py +654 -0
- pystatistics-2.1.0/pystatistics/mvnmle/solvers.py +338 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/test_gpu.py +55 -0
- pystatistics-2.1.0/tests/mvnmle/test_mcar.py +123 -0
- pystatistics-2.1.0/tests/mvnmle/test_mom_mcar.py +169 -0
- pystatistics-2.1.0/tests/mvnmle/test_monotone.py +241 -0
- pystatistics-2.1.0/tests/mvnmle/test_no_silent_fallback.py +96 -0
- pystatistics-2.1.0/tests/mvnmle/test_squarem.py +120 -0
- pystatistics-2.0.1/pystatistics/mvnmle/backends/em.py +0 -330
- pystatistics-2.0.1/pystatistics/mvnmle/mcar_test.py +0 -228
- pystatistics-2.0.1/pystatistics/mvnmle/solvers.py +0 -195
- pystatistics-2.0.1/tests/mvnmle/test_no_silent_fallback.py +0 -55
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.github/workflows/publish.yml +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.github/workflows/trigger-docs-rebuild.yml +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.gitignore +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.release/CHECKLIST.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.release/UNRELEASED.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/.release/release.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/CLAUDE.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/LICENSE +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/DESIGN.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/Forge.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/GPU_BACKEND_NOTES.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/Makefile +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/PYSTATSBIO_CONTEXT.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/ROADMAP.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/_static/custom.css +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/anova.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/conf.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/core.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/descriptive.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/hypothesis.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/index.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/mixed.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/montecarlo.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/mvnmle.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/regression.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/docs/survival.rst +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/GPU_BACKEND_CONVENTION.md +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_contrasts.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_levene.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_posthoc.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_repeated.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/_ss.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/anova/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/capabilities.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/device.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/batched.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/cholesky.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/determinant.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/qr.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/solve.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/linalg/svd.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/optimization/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/optimization/convergence.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/precision.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/timing.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/compute/tolerances.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/datasource.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/exceptions.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/protocols.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/result.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/core/validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/_missing.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/_quantile_types.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/descriptive/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_basis.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_fit.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_gam.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_gcv.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/_smooth.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/backends/_gpu_family.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/backends/gpu_pirls.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/gam/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/_design_factories.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/_p_adjust.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_chisq_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_fisher_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_ks_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_prop_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_t_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_var_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/_wilcox_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/hypothesis/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_deviance.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_pirls.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_pls.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_random_effects.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/_satterthwaite.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mixed/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/_ci.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/_influence.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/montecarlo/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/_likelihood.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/_solver.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/backends/gpu_likelihood.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multinomial/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/_factor.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/_pca.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/_rotation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/multivariate/backends/gpu_pca.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/base.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/gpu_fp32.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/gpu_fp64.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_objectives/parameterizations.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/_utils.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/datasets.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/patterns.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/mvnmle/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/_likelihood.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/_solver.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/backends/gpu_likelihood.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/ordinal/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/py.typed +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/_formatting.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/_glm.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/_linear.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/_nb_theta.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/backends/cpu_glm.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/backends/gpu_glm.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/families.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/regression/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/_cox.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/_discrete.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/_km.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/_logrank.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/backends/cpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/backends/gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/solution.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/survival/solvers.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_acf.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_batch.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_factored.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_fit.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_forecast.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_kalman.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_likelihood.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_arima_order.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_common.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_decomposition.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_differencing.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_ets_fit.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_ets_forecast.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_ets_models.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_stationarity.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/_whittle.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/backends/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/backends/whittle_batch_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/pystatistics/timeseries/backends/whittle_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_contrasts.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_design.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_factorial.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_levene.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_oneway.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_posthoc.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/anova/test_repeated_measures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/benchmark_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/core/test_datasource.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/core/test_exceptions.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/core/test_result.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/core/test_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_cor.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_cov.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_describe.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_missing.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_moments.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_quantile.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/descriptive/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_ancova_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_ancova_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_bonferroni_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_bonferroni_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_eta_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_eta_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_levene_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_levene_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_oneway_balanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_oneway_balanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_oneway_unbalanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_oneway_unbalanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_rm_mixed_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_rm_mixed_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_rm_within_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_rm_within_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_tukey_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_tukey_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_twoway_balanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_twoway_balanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_twoway_unbalanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/anova_twoway_unbalanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/basic_100x3.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/basic_100x3_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/basic_100x3_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/collinear_almost.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/collinear_almost_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/collinear_almost_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_basic_100x5.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_basic_100x5_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_basic_100x5_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_constant_column.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_constant_column_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_constant_column_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_extreme_values.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_extreme_values_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_extreme_values_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_large_1000x10.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_large_1000x10_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_large_1000x10_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_columnwise.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_columnwise_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_columnwise_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_scattered.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_scattered_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_nan_scattered_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_negative_correlation.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_negative_correlation_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_negative_correlation_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_perfect_correlation.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_perfect_correlation_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_perfect_correlation_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_single_column.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_single_column_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_single_column_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_ties.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_ties_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/desc_ties_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/different_scales.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/different_scales_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/different_scales_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_anova_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_descriptive_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_glm_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_hypothesis_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_mixed_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_montecarlo_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/generate_survival_fixtures.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_balanced.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_balanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_balanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_basic.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_basic_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_basic_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_large.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_large_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_large_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_separated.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_separated_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_binomial_separated_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_basic.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_basic_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_basic_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_large.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_large_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_gaussian_large_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_basic.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_basic_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_basic_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_large_counts.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_large_counts_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_large_counts_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_zeros.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_zeros_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/glm_poisson_zeros_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/high_noise.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/high_noise_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/high_noise_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_2x2_yates_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_2x2_yates_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_3x3_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_3x3_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_gof_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_gof_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_gof_unequal_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_chisq_gof_unequal_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_2x2_less_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_2x2_less_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_2x2_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_2x2_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_3x3_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_fisher_3x3_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_ks_onesample_norm_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_ks_onesample_norm_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_ks_twosample_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_ks_twosample_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_prop_onesample_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_prop_onesample_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_prop_twosample_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_prop_twosample_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_onesample_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_onesample_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_paired_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_paired_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_pooled_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_pooled_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_welch_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_t_welch_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_var_basic_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_var_basic_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_wilcox_ranksum_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_wilcox_ranksum_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_wilcox_signed_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/htest_wilcox_signed_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/ill_conditioned.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/ill_conditioned_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/ill_conditioned_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/large_coeffs.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/large_coeffs_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/large_coeffs_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_90_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_90_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_normal_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_normal_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_skewed_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_ci_skewed_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_mean_balanced_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_mean_balanced_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_mean_ordinary_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_mean_ordinary_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_median_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_median_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_variance_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_boot_variance_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_greater_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_greater_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_not_significant_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_not_significant_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_significant_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mc_perm_significant_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/glmm_binomial.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/glmm_poisson.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/lmm_crossed.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/lmm_intercept.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/lmm_ml.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/lmm_no_effect.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/lmm_slope.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/mixed_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/mixed/mixed_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/near_square.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/near_square_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/near_square_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/no_intercept.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/no_intercept_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/no_intercept_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_anova_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_descriptive_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_glm_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_hypothesis_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_mixed_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_montecarlo_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_survival_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_r_validation.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/run_validation.sh +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/small_noise.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/small_noise_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/small_noise_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_breslow_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_breslow_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_single_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_single_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_ties_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_ties_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_two_cov_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_cox_two_cov_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_basic_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_basic_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_heavy_cens_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_heavy_cens_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_loglog_ci_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_loglog_ci_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_no_cens_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_no_cens_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_plain_ci_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_plain_ci_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_ties_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_km_ties_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_peto_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_peto_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_three_group_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_three_group_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_two_group_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/surv_lr_two_group_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/tall_skinny.csv +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/tall_skinny_meta.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/tall_skinny_r_results.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/fixtures/validate_against_r.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/gam/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/gam/test_gam.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_chisq_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_design_split.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_fisher_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_ks_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_p_adjust.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_prop_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_t_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_var_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/hypothesis/test_wilcox_test.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_glmm.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_lmm_crossed.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_lmm_intercept.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_lmm_nested.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_lmm_slope.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_pls.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_random_effects.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mixed/test_satterthwaite.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_batched_solver.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_boot_ci.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_bootstrap.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_influence.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_permutation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/montecarlo/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/multinomial/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/multinomial/test_multinom.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/multivariate/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/multivariate/test_multivariate.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/apple_em_reference.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/apple_reference.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/generate_em_fixtures.R +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_apple.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_complete.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_extreme.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_missvals.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_simple_mcar.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/little_mcar_summary.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/missvals_em_reference.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/missvals_reference.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/references/small_test_reference.json +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/test_em.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/mvnmle/test_mlest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/ordinal/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/ordinal/test_ordinal.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/benchmark.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/benchmark.r +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_fit.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_gamma_nb.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_glm.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_glm_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_glm_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_module_split.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/regression/test_stress_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/conftest.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_coxph.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_discrete_time.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_gpu.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_kaplan_meier.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_logrank.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/survival/test_r_validation.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/test_code_quality.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/timeseries/__init__.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/timeseries/test_acf_stationarity.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/timeseries/test_arima.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/timeseries/test_decomposition.py +0 -0
- {pystatistics-2.0.1 → pystatistics-2.1.0}/tests/timeseries/test_ets.py +0 -0
|
@@ -1,5 +1,218 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
- **`mom_mcar_test`: new method-of-moments MCAR test**
|
|
6
|
+
(``pystatistics/mvnmle/mcar_test.py``). A separate function — not
|
|
7
|
+
a new mode on ``little_mcar_test`` — because the method-of-moments
|
|
8
|
+
variant **is not Little's test**. Little (1988) specifically calls
|
|
9
|
+
for MLE plug-in estimators; swapping in pairwise-deletion sample
|
|
10
|
+
moments gives a statistic of the same shape with different
|
|
11
|
+
asymptotic properties, and calling it Little's test would be a
|
|
12
|
+
polite but concrete lie. The separate function preserves the
|
|
13
|
+
``little_mcar_test`` contract exactly (matches R ``mvnmle`` bit-
|
|
14
|
+
for-bit as before) while giving users a documented fast alternative.
|
|
15
|
+
|
|
16
|
+
End-to-end timings at 15 % MCAR:
|
|
17
|
+
|
|
18
|
+
| dataset | shape | little_mcar_test | mom_mcar_test |
|
|
19
|
+
|----------------|-----------|------------------|---------------|
|
|
20
|
+
| iris | 150 × 4 | 2.9 ms | 0.31 ms |
|
|
21
|
+
| wine | 178 × 13 | 60.9 ms | 2.17 ms |
|
|
22
|
+
| breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
|
|
23
|
+
|
|
24
|
+
For repeated-diagnostic workflows (e.g. an MCAR sweep over 3410
|
|
25
|
+
datasets), this is **1.6 minutes vs ~50 minutes** end-to-end. The
|
|
26
|
+
statistical trade-off is asymptotic efficiency: MoM is consistent
|
|
27
|
+
under the MCAR null but not asymptotically efficient, and the
|
|
28
|
+
finite-sample distribution deviates more from chi-square than
|
|
29
|
+
Little's does. The docstring spells out when to use which:
|
|
30
|
+
diagnostic screens → MoM; regulated submissions or anywhere the
|
|
31
|
+
exact asymptotic distribution matters → Little's.
|
|
32
|
+
|
|
33
|
+
Implementation details:
|
|
34
|
+
- ``_pairwise_deletion_moments``: O(n v^2) pairwise mean and
|
|
35
|
+
covariance via a single matmul. No per-column loop.
|
|
36
|
+
- ``chi_square_mcar_batched_np`` / ``_torch``: fully batched
|
|
37
|
+
chi-square assembly (batched SVD for conditioning,
|
|
38
|
+
well-conditioned patterns through batched solve, ill-conditioned
|
|
39
|
+
patterns through batched pinv as separate groups — no
|
|
40
|
+
per-pattern Python loop).
|
|
41
|
+
- ``backend`` parameter with same size-heuristic + visible-warning
|
|
42
|
+
discipline as the EM path. GPU is supported but does not
|
|
43
|
+
out-perform CPU on any tested shape — MoM's compute is small
|
|
44
|
+
enough that transfer + launch overhead loses to CPU numpy.
|
|
45
|
+
Auto-dispatch warns when this is the case.
|
|
46
|
+
- Honesty: ``MCARTestResult`` gained a ``method`` field so
|
|
47
|
+
downstream code knows which test produced a given result.
|
|
48
|
+
``little_mcar_test`` reports ``"Little (MLE plug-in)"``;
|
|
49
|
+
``mom_mcar_test`` reports ``"Method-of-moments
|
|
50
|
+
(pairwise-deletion plug-in)"``.
|
|
51
|
+
|
|
52
|
+
New tests (``tests/mvnmle/test_mom_mcar.py``, 10 tests):
|
|
53
|
+
name-honesty, MLE-vs-MoM agreement on MCAR data, correct rejection
|
|
54
|
+
on non-MCAR data, all-missing-row handling, speed guard of
|
|
55
|
+
≥ 10× over MLE on breast_cancer.
|
|
56
|
+
|
|
57
|
+
- **Fully-batched device-resident EM on GPU** (``_em_batched.py``
|
|
58
|
+
/ ``_run_em_loop_gpu``). Pre-2.1.0 the "GPU EM" path set up a
|
|
59
|
+
torch device in the constructor but none of the per-iteration
|
|
60
|
+
work actually ran on-device — the numpy E-step ran for every
|
|
61
|
+
backend, which is why pre-2.1.0 benchmarks showed identical CPU
|
|
62
|
+
and GPU timings. This release implements the real thing: one
|
|
63
|
+
batched Cholesky + one batched solve over patterns for the
|
|
64
|
+
regression betas, one batched gather + bmm over all N
|
|
65
|
+
observations for the filled data, two dense gemms for the
|
|
66
|
+
sufficient-statistic accumulators, all on-device. SQUAREM also
|
|
67
|
+
runs fully on-device.
|
|
68
|
+
|
|
69
|
+
EM-only timings (without the MCAR-assembly wrapper):
|
|
70
|
+
|
|
71
|
+
| dataset | shape | CPU EM | GPU EM | speedup |
|
|
72
|
+
|----------------|-----------|----------|----------|---------|
|
|
73
|
+
| wine | 178 × 13 | 38 ms | 24 ms | 1.6× |
|
|
74
|
+
| breast_cancer | 569 × 30 | 2142 ms | 147 ms | 14.6× |
|
|
75
|
+
|
|
76
|
+
Small-data cases (apple, iris, missvals) lose on GPU because
|
|
77
|
+
transfer + launch overhead exceeds the per-iteration work.
|
|
78
|
+
Empirically calibrated heuristic: GPU is worth it when
|
|
79
|
+
``n_obs * n_vars > 1500``.
|
|
80
|
+
|
|
81
|
+
- **Size-heuristic dispatch with Rule-1 visibility** for both EM and
|
|
82
|
+
MoM backends. When ``backend='auto'`` makes a non-obvious choice
|
|
83
|
+
(e.g. picking CPU despite GPU availability because the data are
|
|
84
|
+
too small), a ``UserWarning`` explains the decision and tells
|
|
85
|
+
users how to override. When ``backend='gpu'`` is explicitly
|
|
86
|
+
requested on small data, the request is honored (user knows best)
|
|
87
|
+
but a warning notes that CPU would likely be faster. No silent
|
|
88
|
+
fallbacks anywhere. New tests pin these behaviours.
|
|
89
|
+
|
|
90
|
+
- **Monotone-missingness closed-form MLE** (Anderson 1957; new
|
|
91
|
+
``pystatistics.mvnmle._monotone``). When the missingness pattern
|
|
92
|
+
is monotone — when variables can be ordered such that each
|
|
93
|
+
observation's missing entries form a contiguous suffix — the MVN
|
|
94
|
+
MLE has a closed form via a chain of OLS regressions, with no
|
|
95
|
+
iteration. Common on longitudinal data with attrition, panel
|
|
96
|
+
surveys with dropout, and most sequentially-administered
|
|
97
|
+
instruments. New public helpers:
|
|
98
|
+
|
|
99
|
+
- ``pystatistics.mvnmle.is_monotone(data) -> bool``
|
|
100
|
+
- ``pystatistics.mvnmle.monotone_permutation(data) -> ndarray | None``
|
|
101
|
+
- ``pystatistics.mvnmle.mlest_monotone_closed_form(data) -> (mu, sigma, n)``
|
|
102
|
+
- ``mlest(data, algorithm='monotone')`` routes through the
|
|
103
|
+
closed-form; raises ``ValidationError`` if the data are not
|
|
104
|
+
monotone (Rule 1: no silent dispatch). Users who want
|
|
105
|
+
"use the closed form when applicable, fall back otherwise"
|
|
106
|
+
should call ``is_monotone`` first and branch explicitly.
|
|
107
|
+
|
|
108
|
+
The closed-form is the exact MLE (no tolerance-bounded
|
|
109
|
+
approximation), matches R ``mvnmle`` reference output on both
|
|
110
|
+
``apple`` and ``missvals`` to machine precision, and is
|
|
111
|
+
dramatically faster than iterative algorithms at larger v
|
|
112
|
+
(a 1500 × 20 monotone dataset completes in ~2 ms vs EM's
|
|
113
|
+
~40 ms). For non-monotone random MCAR data (the common case
|
|
114
|
+
in MCAR diagnostic use), detection is cheap (~O(v² n)) and
|
|
115
|
+
correctly returns False so iterative algorithms run.
|
|
116
|
+
|
|
117
|
+
New tests (``tests/mvnmle/test_monotone.py``, 12 tests):
|
|
118
|
+
detection true-positive / true-negative on several canonical
|
|
119
|
+
shapes; closed-form vs EM agreement; permutation invariance;
|
|
120
|
+
non-monotone data raises; performance guard at v=20.
|
|
121
|
+
|
|
122
|
+
- **EM MLE: substantial real-data speedup via batched per-pattern
|
|
123
|
+
linear algebra + SQUAREM acceleration** (Project Lacuna-driven).
|
|
124
|
+
|
|
125
|
+
End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
|
|
126
|
+
|
|
127
|
+
| dataset | shape | 2.0.1 | 2.1.0 | speedup |
|
|
128
|
+
|----------------|-----------|----------|----------|---------|
|
|
129
|
+
| apple | 18 × 2 | 1.9 ms | 2.0 ms | flat |
|
|
130
|
+
| missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
|
|
131
|
+
| iris | 150 × 4 | 2.8 ms | 2.8 ms | flat |
|
|
132
|
+
| wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
|
|
133
|
+
| breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
|
|
134
|
+
|
|
135
|
+
For workloads that run MCAR repeatedly over many datasets
|
|
136
|
+
(e.g. a 3410-entry MCAR sweep), this is roughly a 1-hour reduction
|
|
137
|
+
per full pass at Lacuna's current scale.
|
|
138
|
+
|
|
139
|
+
Three changes stack:
|
|
140
|
+
|
|
141
|
+
1. **Batched per-pattern conditional parameters** (new
|
|
142
|
+
``pystatistics.mvnmle.backends._em_batched``). The E-step used
|
|
143
|
+
to loop in Python over missingness patterns, issuing a scalar
|
|
144
|
+
Cholesky + triangular solve per pattern. It now stacks all P
|
|
145
|
+
pattern-sigma submatrices into a single
|
|
146
|
+
``(P, v_max, v_max)`` tensor (identity-padded in the unused
|
|
147
|
+
slots so the Cholesky stays well-defined) and runs one batched
|
|
148
|
+
Cholesky + one batched solve for the whole iteration. The
|
|
149
|
+
accumulator loop over patterns remains in Python because
|
|
150
|
+
``n_k`` varies and full observation-level padding hurt more
|
|
151
|
+
than it helped on the representative shapes we benchmarked.
|
|
152
|
+
|
|
153
|
+
2. **SQUAREM acceleration** (Varadhan & Roland 2008; new
|
|
154
|
+
``pystatistics.mvnmle.backends._squarem``). EM's linear
|
|
155
|
+
convergence is sped up by a Steffensen-style extrapolation of
|
|
156
|
+
three consecutive EM iterates, safeguarded by a monotonicity
|
|
157
|
+
check on the observed-data log-likelihood. Typical effect on
|
|
158
|
+
well-behaved EM problems: 2–4× reduction in underlying
|
|
159
|
+
EM-step-equivalents. Preserves the MLE — the convergence
|
|
160
|
+
point is unchanged, only the path is shorter. On by default
|
|
161
|
+
via a new ``accelerate=True`` kwarg on ``EMBackend.solve``;
|
|
162
|
+
pass ``accelerate=False`` for the plain-EM reference path.
|
|
163
|
+
|
|
164
|
+
3. **Fully batched observed-data log-likelihood**
|
|
165
|
+
(``compute_loglik_batched_np``). The SQUAREM monotonicity
|
|
166
|
+
safeguard calls the log-likelihood often, so that path
|
|
167
|
+
needed to be cheap. The implementation now does one batched
|
|
168
|
+
Cholesky over all patterns for log-determinants and one
|
|
169
|
+
batched solve across all N observations for the quadratic-
|
|
170
|
+
form contribution — no per-pattern Python loop.
|
|
171
|
+
|
|
172
|
+
- **Benchmark harness** (``benchmarks/mvnmle_bench.py``). Runs the
|
|
173
|
+
five reference shapes (apple, missvals, iris, wine,
|
|
174
|
+
breast_cancer) across the (algorithm, backend) matrix and
|
|
175
|
+
prints wall-clock / iteration counts per case. Use
|
|
176
|
+
``--quick`` to skip the BFGS cases that don't converge on
|
|
177
|
+
high-$v$ data; ``--tag`` labels a run for diff against prior
|
|
178
|
+
baselines.
|
|
179
|
+
|
|
180
|
+
- **Documented why direct-BFGS is not always the right default.**
|
|
181
|
+
Internal notes and the 2.0.0 / 2.0.1 release narrative already
|
|
182
|
+
covered why ``algorithm='em'`` became the ``little_mcar_test``
|
|
183
|
+
default; this release adds the story of why batching helps EM
|
|
184
|
+
significantly but does *not* rescue direct-BFGS on realistic
|
|
185
|
+
high-$v$ data (layer-3 Hessian conditioning is parameterization-
|
|
186
|
+
invariant; batching only addresses layer-1 launch overhead).
|
|
187
|
+
See ``GPU_BACKEND_CONVENTION.md`` Section 0 for the "when to
|
|
188
|
+
add a GPU backend and when not" rule that drove the 2.0.1
|
|
189
|
+
cleanup; this release extends that logic with
|
|
190
|
+
"accelerating the algorithm by reducing iteration count
|
|
191
|
+
(SQUAREM) is cheaper than accelerating each iteration."
|
|
192
|
+
|
|
193
|
+
- **Finding: the "GPU EM" backend was never actually running on
|
|
194
|
+
GPU.** The ``device='cuda'`` / ``'mps'`` constructor flag set
|
|
195
|
+
up ``self._torch`` but none of ``_e_step`` / ``_m_step`` /
|
|
196
|
+
``_compute_loglik`` used it — the numpy path ran for every
|
|
197
|
+
backend. That's why pre-2.1.0 benchmarks showed identical
|
|
198
|
+
CPU and GPU EM timings. We attempted to implement a real
|
|
199
|
+
device-resident EM loop and found it was slower than CPU for
|
|
200
|
+
all the shapes we care about (per-pattern kernel-launch
|
|
201
|
+
overhead dominates the small per-pattern matrix work). The
|
|
202
|
+
honest answer for now is that GPU EM stays CPU-equivalent
|
|
203
|
+
by design; a future release may revisit with fully N-parallel
|
|
204
|
+
observation-level batching if a workload appears where the
|
|
205
|
+
GPU can actually win. This behaviour is unchanged from prior
|
|
206
|
+
releases — we're just documenting what was already true.
|
|
207
|
+
|
|
208
|
+
- **SQUAREM test coverage** (new ``tests/mvnmle/test_squarem.py``).
|
|
209
|
+
Four tests pinning the invariants: same MLE as plain EM on
|
|
210
|
+
apple; substantially fewer EM-equivalent steps on missvals;
|
|
211
|
+
monotonicity of log-likelihood preserved across iteration
|
|
212
|
+
caps; same MLE as plain EM on a realistic shape (sklearn
|
|
213
|
+
wine with 15 % MCAR).
|
|
214
|
+
|
|
215
|
+
|
|
3
216
|
## 2.0.1
|
|
4
217
|
|
|
5
218
|
- **GPU Backend Convention: codified when NOT to add a GPU backend**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pystatistics
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: GPU-accelerated statistical computing for Python
|
|
5
5
|
Project-URL: Homepage, https://sgcx.org/technology/pystatistics/
|
|
6
6
|
Project-URL: Documentation, https://sgcx.org/docs/pystatistics/
|
|
@@ -51,6 +51,108 @@ GPU-accelerated statistical computing for Python.
|
|
|
51
51
|
|
|
52
52
|
## What's New
|
|
53
53
|
|
|
54
|
+
### 2.1.0 — Real-data EM speedup + monotone closed-form MLE
|
|
55
|
+
|
|
56
|
+
Dogfooding via Project Lacuna surfaced that ``little_mcar_test`` on
|
|
57
|
+
realistic tabular data (sklearn's iris / wine / breast_cancer with
|
|
58
|
+
random MCAR injection) was bottlenecked by EM: the E-step was a
|
|
59
|
+
Python loop over missingness patterns, and each SQUAREM-style
|
|
60
|
+
safeguard pass re-ran a per-pattern log-likelihood. This release
|
|
61
|
+
batches both and adds Varadhan & Roland's SQUAREM acceleration.
|
|
62
|
+
|
|
63
|
+
End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
|
|
64
|
+
|
|
65
|
+
| dataset | shape | 2.0.1 | 2.1.0 | speedup |
|
|
66
|
+
|----------------|-----------|----------|----------|---------|
|
|
67
|
+
| missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
|
|
68
|
+
| wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
|
|
69
|
+
| breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
|
|
70
|
+
|
|
71
|
+
For repeated-diagnostic workflows (e.g. an MCAR sweep over several
|
|
72
|
+
thousand datasets), this turns a 3-hour run into a 2-hour run.
|
|
73
|
+
|
|
74
|
+
Three stacked improvements, all preserving bit-equivalence on the R
|
|
75
|
+
mvnmle reference cases (apple, missvals):
|
|
76
|
+
|
|
77
|
+
- **Batched per-pattern conditional parameters.** The E-step's
|
|
78
|
+
per-pattern Cholesky + triangular solve now runs as a single
|
|
79
|
+
batched kernel pair across all missingness patterns. The
|
|
80
|
+
unused padding slots are identity-filled so the Cholesky stays
|
|
81
|
+
well-defined.
|
|
82
|
+
- **SQUAREM acceleration on top of EM.** Three EM steps + one
|
|
83
|
+
Steffensen-style extrapolation, safeguarded by a monotonicity
|
|
84
|
+
check on the observed-data log-likelihood. Typical effect:
|
|
85
|
+
2–4× fewer EM-step equivalents to convergence. Convergence
|
|
86
|
+
point is the same MLE — only the path is shorter. On by
|
|
87
|
+
default; ``EMBackend.solve(..., accelerate=False)`` recovers
|
|
88
|
+
the plain-EM reference.
|
|
89
|
+
- **Fully batched log-likelihood.** The SQUAREM monotonicity
|
|
90
|
+
check calls ``loglik`` often, so it was batched too — one
|
|
91
|
+
Cholesky over all patterns, one solve across all N
|
|
92
|
+
observations, no per-pattern Python loop.
|
|
93
|
+
|
|
94
|
+
**`mom_mcar_test`: fast method-of-moments MCAR test.** A new *separate
|
|
95
|
+
function* (not a mode on ``little_mcar_test``, because the MoM variant
|
|
96
|
+
is not Little's test) that uses pairwise-deletion sample moments
|
|
97
|
+
instead of MLE plug-in. The test is consistent under MCAR but not
|
|
98
|
+
asymptotically efficient, trading a small amount of statistical
|
|
99
|
+
efficiency for dramatic speed. At 15 % MCAR on sklearn demos:
|
|
100
|
+
|
|
101
|
+
| dataset | shape | little_mcar_test | mom_mcar_test |
|
|
102
|
+
|----------------|-----------|------------------|---------------|
|
|
103
|
+
| iris | 150 × 4 | 2.9 ms | 0.31 ms |
|
|
104
|
+
| wine | 178 × 13 | 60.9 ms | 2.17 ms |
|
|
105
|
+
| breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
|
|
106
|
+
|
|
107
|
+
For a 3410-dataset MCAR sweep: **~50 minutes → ~1.6 minutes**. Use
|
|
108
|
+
``little_mcar_test`` when you need Little 1988's asymptotic
|
|
109
|
+
distribution exactly (regulated submissions, citing R reference);
|
|
110
|
+
use ``mom_mcar_test`` for high-throughput diagnostic screens. The
|
|
111
|
+
``MCARTestResult.method`` field records which test produced a given
|
|
112
|
+
result so downstream code can disambiguate without tracking the
|
|
113
|
+
calling function.
|
|
114
|
+
|
|
115
|
+
**Fully-batched device-resident EM on GPU.** Pre-2.1.0 the
|
|
116
|
+
``device='cuda'`` EM path set up a torch device but never used it —
|
|
117
|
+
numpy ran for every backend. This release implements a real
|
|
118
|
+
device-resident loop with fully batched E-step / M-step / log-
|
|
119
|
+
likelihood, SQUAREM acceleration on top, all on device. On breast-
|
|
120
|
+
cancer-scale (569 × 30) EM drops from 2142 ms CPU to 147 ms GPU
|
|
121
|
+
(14.6×). Small data remains CPU-faster; an empirical size heuristic
|
|
122
|
+
(``n * v >= 1500``) with visible dispatch warnings keeps this
|
|
123
|
+
correct in user-facing behaviour.
|
|
124
|
+
|
|
125
|
+
**Monotone-missingness closed-form MLE** (Anderson 1957). Longitudinal
|
|
126
|
+
cohorts with attrition, panel surveys with dropout, and most
|
|
127
|
+
sequentially-administered instruments produce *monotone* missingness
|
|
128
|
+
— the variables can be ordered such that each observation's missing
|
|
129
|
+
entries form a contiguous suffix. When the pattern is monotone, the
|
|
130
|
+
MVN MLE has a closed form via a chain of OLS regressions, with no
|
|
131
|
+
iteration. New helpers: ``mvnmle.is_monotone(data)``,
|
|
132
|
+
``mvnmle.monotone_permutation(data)``, and
|
|
133
|
+
``mlest(data, algorithm='monotone')``. The closed-form matches R
|
|
134
|
+
``mvnmle`` bit-for-bit on canonical datasets and is orders of
|
|
135
|
+
magnitude faster than EM on larger-v longitudinal data. Per Rule 1
|
|
136
|
+
the algorithm raises on non-monotone input rather than silently
|
|
137
|
+
falling back — call ``is_monotone`` first if you want conditional
|
|
138
|
+
dispatch.
|
|
139
|
+
|
|
140
|
+
Also in this release:
|
|
141
|
+
|
|
142
|
+
- **Benchmark harness** under ``benchmarks/mvnmle_bench.py`` for
|
|
143
|
+
tracking wall-clock and iteration counts across the reference
|
|
144
|
+
shapes; use the ``--tag`` flag to label a baseline for diff
|
|
145
|
+
against future changes.
|
|
146
|
+
- **Documented finding**: the ``device='cuda'`` EM path was never
|
|
147
|
+
actually running on the GPU prior to this release — it stored
|
|
148
|
+
a torch device but never used it. We tried to wire up a real
|
|
149
|
+
device-resident loop and found GPU is slower than CPU for all
|
|
150
|
+
shapes we tested (per-pattern launch overhead still dominates
|
|
151
|
+
the tiny per-pattern matrix work). GPU EM therefore remains
|
|
152
|
+
CPU-equivalent by design; a future release will revisit if a
|
|
153
|
+
workload appears where full observation-level batching makes
|
|
154
|
+
GPU actually win.
|
|
155
|
+
|
|
54
156
|
### 2.0.1 — GPU-backend exposure gaps and a convention rule
|
|
55
157
|
|
|
56
158
|
Two public functions had GPU-capable inner calls but no `backend=`
|
|
@@ -4,6 +4,108 @@ GPU-accelerated statistical computing for Python.
|
|
|
4
4
|
|
|
5
5
|
## What's New
|
|
6
6
|
|
|
7
|
+
### 2.1.0 — Real-data EM speedup + monotone closed-form MLE
|
|
8
|
+
|
|
9
|
+
Dogfooding via Project Lacuna surfaced that ``little_mcar_test`` on
|
|
10
|
+
realistic tabular data (sklearn's iris / wine / breast_cancer with
|
|
11
|
+
random MCAR injection) was bottlenecked by EM: the E-step was a
|
|
12
|
+
Python loop over missingness patterns, and each SQUAREM-style
|
|
13
|
+
safeguard pass re-ran a per-pattern log-likelihood. This release
|
|
14
|
+
batches both and adds Varadhan & Roland's SQUAREM acceleration.
|
|
15
|
+
|
|
16
|
+
End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
|
|
17
|
+
|
|
18
|
+
| dataset | shape | 2.0.1 | 2.1.0 | speedup |
|
|
19
|
+
|----------------|-----------|----------|----------|---------|
|
|
20
|
+
| missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
|
|
21
|
+
| wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
|
|
22
|
+
| breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
|
|
23
|
+
|
|
24
|
+
For repeated-diagnostic workflows (e.g. an MCAR sweep over several
|
|
25
|
+
thousand datasets), this turns a 3-hour run into a 2-hour run.
|
|
26
|
+
|
|
27
|
+
Three stacked improvements, all preserving bit-equivalence on the R
|
|
28
|
+
mvnmle reference cases (apple, missvals):
|
|
29
|
+
|
|
30
|
+
- **Batched per-pattern conditional parameters.** The E-step's
|
|
31
|
+
per-pattern Cholesky + triangular solve now runs as a single
|
|
32
|
+
batched kernel pair across all missingness patterns. The
|
|
33
|
+
unused padding slots are identity-filled so the Cholesky stays
|
|
34
|
+
well-defined.
|
|
35
|
+
- **SQUAREM acceleration on top of EM.** Three EM steps + one
|
|
36
|
+
Steffensen-style extrapolation, safeguarded by a monotonicity
|
|
37
|
+
check on the observed-data log-likelihood. Typical effect:
|
|
38
|
+
2–4× fewer EM-step equivalents to convergence. Convergence
|
|
39
|
+
point is the same MLE — only the path is shorter. On by
|
|
40
|
+
default; ``EMBackend.solve(..., accelerate=False)`` recovers
|
|
41
|
+
the plain-EM reference.
|
|
42
|
+
- **Fully batched log-likelihood.** The SQUAREM monotonicity
|
|
43
|
+
check calls ``loglik`` often, so it was batched too — one
|
|
44
|
+
Cholesky over all patterns, one solve across all N
|
|
45
|
+
observations, no per-pattern Python loop.
|
|
46
|
+
|
|
47
|
+
**`mom_mcar_test`: fast method-of-moments MCAR test.** A new *separate
|
|
48
|
+
function* (not a mode on ``little_mcar_test``, because the MoM variant
|
|
49
|
+
is not Little's test) that uses pairwise-deletion sample moments
|
|
50
|
+
instead of MLE plug-in. The test is consistent under MCAR but not
|
|
51
|
+
asymptotically efficient, trading a small amount of statistical
|
|
52
|
+
efficiency for dramatic speed. At 15 % MCAR on sklearn demos:
|
|
53
|
+
|
|
54
|
+
| dataset | shape | little_mcar_test | mom_mcar_test |
|
|
55
|
+
|----------------|-----------|------------------|---------------|
|
|
56
|
+
| iris | 150 × 4 | 2.9 ms | 0.31 ms |
|
|
57
|
+
| wine | 178 × 13 | 60.9 ms | 2.17 ms |
|
|
58
|
+
| breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
|
|
59
|
+
|
|
60
|
+
For a 3410-dataset MCAR sweep: **~50 minutes → ~1.6 minutes**. Use
|
|
61
|
+
``little_mcar_test`` when you need Little 1988's asymptotic
|
|
62
|
+
distribution exactly (regulated submissions, citing R reference);
|
|
63
|
+
use ``mom_mcar_test`` for high-throughput diagnostic screens. The
|
|
64
|
+
``MCARTestResult.method`` field records which test produced a given
|
|
65
|
+
result so downstream code can disambiguate without tracking the
|
|
66
|
+
calling function.
|
|
67
|
+
|
|
68
|
+
**Fully-batched device-resident EM on GPU.** Pre-2.1.0 the
|
|
69
|
+
``device='cuda'`` EM path set up a torch device but never used it —
|
|
70
|
+
numpy ran for every backend. This release implements a real
|
|
71
|
+
device-resident loop with fully batched E-step / M-step / log-
|
|
72
|
+
likelihood, SQUAREM acceleration on top, all on device. On breast-
|
|
73
|
+
cancer-scale (569 × 30) EM drops from 2142 ms CPU to 147 ms GPU
|
|
74
|
+
(14.6×). Small data remains CPU-faster; an empirical size heuristic
|
|
75
|
+
(``n * v >= 1500``) with visible dispatch warnings keeps this
|
|
76
|
+
correct in user-facing behaviour.
|
|
77
|
+
|
|
78
|
+
**Monotone-missingness closed-form MLE** (Anderson 1957). Longitudinal
|
|
79
|
+
cohorts with attrition, panel surveys with dropout, and most
|
|
80
|
+
sequentially-administered instruments produce *monotone* missingness
|
|
81
|
+
— the variables can be ordered such that each observation's missing
|
|
82
|
+
entries form a contiguous suffix. When the pattern is monotone, the
|
|
83
|
+
MVN MLE has a closed form via a chain of OLS regressions, with no
|
|
84
|
+
iteration. New helpers: ``mvnmle.is_monotone(data)``,
|
|
85
|
+
``mvnmle.monotone_permutation(data)``, and
|
|
86
|
+
``mlest(data, algorithm='monotone')``. The closed-form matches R
|
|
87
|
+
``mvnmle`` bit-for-bit on canonical datasets and is orders of
|
|
88
|
+
magnitude faster than EM on larger-v longitudinal data. Per Rule 1
|
|
89
|
+
the algorithm raises on non-monotone input rather than silently
|
|
90
|
+
falling back — call ``is_monotone`` first if you want conditional
|
|
91
|
+
dispatch.
|
|
92
|
+
|
|
93
|
+
Also in this release:
|
|
94
|
+
|
|
95
|
+
- **Benchmark harness** under ``benchmarks/mvnmle_bench.py`` for
|
|
96
|
+
tracking wall-clock and iteration counts across the reference
|
|
97
|
+
shapes; use the ``--tag`` flag to label a baseline for diff
|
|
98
|
+
against future changes.
|
|
99
|
+
- **Documented finding**: the ``device='cuda'`` EM path was never
|
|
100
|
+
actually running on the GPU prior to this release — it stored
|
|
101
|
+
a torch device but never used it. We tried to wire up a real
|
|
102
|
+
device-resident loop and found GPU is slower than CPU for all
|
|
103
|
+
shapes we tested (per-pattern launch overhead still dominates
|
|
104
|
+
the tiny per-pattern matrix work). GPU EM therefore remains
|
|
105
|
+
CPU-equivalent by design; a future release will revisit if a
|
|
106
|
+
workload appears where full observation-level batching makes
|
|
107
|
+
GPU actually win.
|
|
108
|
+
|
|
7
109
|
### 2.0.1 — GPU-backend exposure gaps and a convention rule
|
|
8
110
|
|
|
9
111
|
Two public functions had GPU-capable inner calls but no `backend=`
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""Benchmark harness for pystatistics.mvnmle.
|
|
2
|
+
|
|
3
|
+
Measures wall-clock + iteration count across a representative
|
|
4
|
+
spectrum of shapes, for every (algorithm, backend) combination.
|
|
5
|
+
|
|
6
|
+
Shapes:
|
|
7
|
+
apple 2-var, 18 obs — R-mvnmle reference case
|
|
8
|
+
missvals 5-var, 13 obs — R-mvnmle reference case
|
|
9
|
+
iris 4-var, 150 obs, synthetic MCAR — sklearn demo
|
|
10
|
+
wine 13-var, 178 obs, synthetic MCAR — sklearn demo
|
|
11
|
+
(the Project Lacuna canary: 100+ patterns)
|
|
12
|
+
breast 30-var, 569 obs, synthetic MCAR — sklearn demo
|
|
13
|
+
(Lacuna's real per-entry workload)
|
|
14
|
+
|
|
15
|
+
Run:
|
|
16
|
+
python benchmarks/mvnmle_bench.py # full sweep
|
|
17
|
+
python benchmarks/mvnmle_bench.py --quick # skip slow BFGS cases
|
|
18
|
+
python benchmarks/mvnmle_bench.py --tag baseline > baseline.txt
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import argparse
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
from dataclasses import dataclass
|
|
26
|
+
|
|
27
|
+
import numpy as np
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
SEED = 0
|
|
31
|
+
MISSING_RATE = 0.15
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class Case:
|
|
36
|
+
name: str
|
|
37
|
+
data_fn: object
|
|
38
|
+
shape_hint: str
|
|
39
|
+
slow_bfgs: bool
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _load_apple():
|
|
43
|
+
from pystatistics.mvnmle.datasets import apple
|
|
44
|
+
return apple.copy()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _load_missvals():
|
|
48
|
+
from pystatistics.mvnmle.datasets import missvals
|
|
49
|
+
return missvals.copy()
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _load_sklearn(loader_name):
|
|
53
|
+
from sklearn import datasets as sk
|
|
54
|
+
X = getattr(sk, f"load_{loader_name}")().data.astype(float).copy()
|
|
55
|
+
rng = np.random.default_rng(SEED)
|
|
56
|
+
X[rng.random(X.shape) < MISSING_RATE] = np.nan
|
|
57
|
+
X = X[~np.all(np.isnan(X), axis=1)]
|
|
58
|
+
return X
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
CASES = [
|
|
62
|
+
Case("apple", _load_apple, "18 x 2", False),
|
|
63
|
+
Case("missvals", _load_missvals, "13 x 5", False),
|
|
64
|
+
Case("iris", lambda: _load_sklearn("iris"), "150 x 4", False),
|
|
65
|
+
Case("wine", lambda: _load_sklearn("wine"), "178 x 13", True),
|
|
66
|
+
Case("breast", lambda: _load_sklearn("breast_cancer"),"569 x 30", True),
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _gpu_available():
|
|
71
|
+
try:
|
|
72
|
+
import torch
|
|
73
|
+
return torch.cuda.is_available()
|
|
74
|
+
except ImportError:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def bench_one(data, algorithm, backend, max_iter, repeat=1):
|
|
79
|
+
"""Return dict with time_ms, n_iter, converged, loglik, err.
|
|
80
|
+
|
|
81
|
+
Never raises — any exception becomes err=type-name.
|
|
82
|
+
"""
|
|
83
|
+
from pystatistics.mvnmle import mlest
|
|
84
|
+
|
|
85
|
+
# Warmup
|
|
86
|
+
try:
|
|
87
|
+
_ = mlest(data, algorithm=algorithm, backend=backend,
|
|
88
|
+
max_iter=max_iter, verbose=False)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
return {"time_ms": None, "n_iter": None, "converged": False,
|
|
91
|
+
"loglik": None, "err": type(e).__name__}
|
|
92
|
+
|
|
93
|
+
times = []
|
|
94
|
+
n_iter = None
|
|
95
|
+
converged = None
|
|
96
|
+
loglik = None
|
|
97
|
+
for _ in range(repeat):
|
|
98
|
+
t = time.perf_counter()
|
|
99
|
+
try:
|
|
100
|
+
r = mlest(data, algorithm=algorithm, backend=backend,
|
|
101
|
+
max_iter=max_iter, verbose=False)
|
|
102
|
+
times.append(time.perf_counter() - t)
|
|
103
|
+
n_iter = r.n_iter
|
|
104
|
+
converged = r.converged
|
|
105
|
+
loglik = r.loglik
|
|
106
|
+
except Exception as e:
|
|
107
|
+
return {"time_ms": None, "n_iter": None, "converged": False,
|
|
108
|
+
"loglik": None, "err": type(e).__name__}
|
|
109
|
+
median_ms = 1000 * float(np.median(times))
|
|
110
|
+
return {"time_ms": median_ms, "n_iter": n_iter, "converged": converged,
|
|
111
|
+
"loglik": loglik, "err": None}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def bench_mcar_one(data, backend, repeat=1):
|
|
115
|
+
"""Benchmark little_mcar_test end-to-end (what Lacuna actually calls)."""
|
|
116
|
+
from pystatistics.mvnmle import little_mcar_test
|
|
117
|
+
import warnings
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
with warnings.catch_warnings():
|
|
121
|
+
warnings.simplefilter("ignore")
|
|
122
|
+
_ = little_mcar_test(data, backend=backend)
|
|
123
|
+
except Exception as e:
|
|
124
|
+
return {"time_ms": None, "err": type(e).__name__, "stat": None}
|
|
125
|
+
|
|
126
|
+
times = []
|
|
127
|
+
stat = None
|
|
128
|
+
for _ in range(repeat):
|
|
129
|
+
t = time.perf_counter()
|
|
130
|
+
try:
|
|
131
|
+
with warnings.catch_warnings():
|
|
132
|
+
warnings.simplefilter("ignore")
|
|
133
|
+
r = little_mcar_test(data, backend=backend)
|
|
134
|
+
times.append(time.perf_counter() - t)
|
|
135
|
+
stat = r.statistic
|
|
136
|
+
except Exception as e:
|
|
137
|
+
return {"time_ms": None, "err": type(e).__name__, "stat": None}
|
|
138
|
+
return {"time_ms": 1000 * float(np.median(times)),
|
|
139
|
+
"err": None, "stat": stat}
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def main():
|
|
143
|
+
ap = argparse.ArgumentParser()
|
|
144
|
+
ap.add_argument("--quick", action="store_true",
|
|
145
|
+
help="skip BFGS on known-slow cases")
|
|
146
|
+
ap.add_argument("--tag", default="",
|
|
147
|
+
help="label printed with each row (e.g. 'baseline')")
|
|
148
|
+
ap.add_argument("--repeat", type=int, default=1,
|
|
149
|
+
help="repetitions per case; reports median")
|
|
150
|
+
ap.add_argument("--max-iter", type=int, default=500)
|
|
151
|
+
args = ap.parse_args()
|
|
152
|
+
|
|
153
|
+
gpu = _gpu_available()
|
|
154
|
+
backends = ["cpu"] + (["gpu"] if gpu else [])
|
|
155
|
+
print(f"# GPU available: {gpu}")
|
|
156
|
+
print(f"# Missing rate: {MISSING_RATE}, seed: {SEED}")
|
|
157
|
+
print(f"# Tag: {args.tag!r}")
|
|
158
|
+
print()
|
|
159
|
+
|
|
160
|
+
header = f"{'case':<10} {'shape':<10} {'algo':<7} {'backend':<4} {'time_ms':>10} {'n_iter':>7} {'conv':>5} {'err':<15}"
|
|
161
|
+
print(header)
|
|
162
|
+
print("-" * len(header))
|
|
163
|
+
|
|
164
|
+
for case in CASES:
|
|
165
|
+
data = case.data_fn()
|
|
166
|
+
for algorithm in ("em", "direct"):
|
|
167
|
+
for backend in backends:
|
|
168
|
+
if args.quick and algorithm == "direct" and case.slow_bfgs:
|
|
169
|
+
continue
|
|
170
|
+
r = bench_one(data, algorithm, backend,
|
|
171
|
+
max_iter=args.max_iter, repeat=args.repeat)
|
|
172
|
+
t = f"{r['time_ms']:.1f}" if r["time_ms"] is not None else "--"
|
|
173
|
+
ni = r["n_iter"] if r["n_iter"] is not None else "--"
|
|
174
|
+
cv = "y" if r["converged"] else ("--" if r["converged"] is None else "n")
|
|
175
|
+
err = r["err"] or ""
|
|
176
|
+
print(f"{case.name:<10} {case.shape_hint:<10} {algorithm:<7} "
|
|
177
|
+
f"{backend:<4} {t:>10} {ni:>7} {cv:>5} {err:<15}")
|
|
178
|
+
|
|
179
|
+
print()
|
|
180
|
+
print("# little_mcar_test end-to-end timings:")
|
|
181
|
+
print(f"{'case':<10} {'shape':<10} {'backend':<4} {'time_ms':>10}")
|
|
182
|
+
print("-" * 40)
|
|
183
|
+
for case in CASES:
|
|
184
|
+
data = case.data_fn()
|
|
185
|
+
for backend in backends:
|
|
186
|
+
r = bench_mcar_one(data, backend, repeat=args.repeat)
|
|
187
|
+
t = f"{r['time_ms']:.1f}" if r["time_ms"] is not None else "--"
|
|
188
|
+
err = r["err"] or ""
|
|
189
|
+
print(f"{case.name:<10} {case.shape_hint:<10} {backend:<4} "
|
|
190
|
+
f"{t:>10} {err}")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|