pystatistics 2.3.0__tar.gz → 3.0.1__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-3.0.1/.release/CHECKLIST.md +84 -0
- pystatistics-3.0.1/.release/release.py +312 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/CHANGELOG.md +177 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/CLAUDE.md +87 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/PKG-INFO +168 -359
- pystatistics-3.0.1/README.md +459 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pyproject.toml +2 -8
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/__init__.py +1 -1
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/backends/gpu_pirls.py +86 -3
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/__init__.py +0 -2
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/_em_batched.py +9 -8
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/_em_batched_np.py +5 -117
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/_em_batched_torch.py +5 -111
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/em.py +5 -5
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/mcar_test.py +7 -310
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/gam/test_gam.py +33 -1
- pystatistics-2.3.0/.release/CHECKLIST.md +0 -38
- pystatistics-2.3.0/.release/release.py +0 -439
- pystatistics-2.3.0/README.md +0 -648
- pystatistics-2.3.0/pystatistics/nonparametric_mcar/__init__.py +0 -45
- pystatistics-2.3.0/pystatistics/nonparametric_mcar/hsic.py +0 -237
- pystatistics-2.3.0/pystatistics/nonparametric_mcar/missmech.py +0 -254
- pystatistics-2.3.0/pystatistics/nonparametric_mcar/propensity.py +0 -285
- pystatistics-2.3.0/pystatistics/nonparametric_mcar/result.py +0 -67
- pystatistics-2.3.0/tests/mvnmle/test_mom_mcar.py +0 -169
- pystatistics-2.3.0/tests/nonparametric_mcar/test_hsic.py +0 -112
- pystatistics-2.3.0/tests/nonparametric_mcar/test_missmech.py +0 -110
- pystatistics-2.3.0/tests/nonparametric_mcar/test_propensity.py +0 -145
- pystatistics-2.3.0/tests/timeseries/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/.github/workflows/publish.yml +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/.github/workflows/trigger-docs-rebuild.yml +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/.gitignore +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/.release/UNRELEASED.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/LICENSE +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/benchmarks/mvnmle_bench.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/DESIGN.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/Forge.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/GPU_BACKEND_NOTES.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/Makefile +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/PYSTATSBIO_CONTEXT.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/ROADMAP.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/_static/custom.css +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/anova.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/conf.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/core.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/descriptive.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/hypothesis.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/index.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/mixed.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/montecarlo.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/mvnmle.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/regression.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/docs/survival.rst +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/GPU_BACKEND_CONVENTION.md +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_contrasts.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_levene.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_posthoc.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_repeated.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/_ss.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/anova/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/capabilities.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/device.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/batched.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/cholesky.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/determinant.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/qr.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/solve.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/linalg/svd.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/optimization/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/optimization/convergence.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/precision.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/timing.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/compute/tolerances.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/datasource.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/exceptions.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/protocols.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/result.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/core/validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/_missing.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/_quantile_types.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/descriptive/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_basis.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_fit.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_gam.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_gcv.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/_smooth.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/backends/_gpu_family.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/gam/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/_design_factories.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/_p_adjust.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_chisq_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_fisher_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_ks_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_prop_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_t_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_var_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/_wilcox_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/hypothesis/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_deviance.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_pirls.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_pls.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_random_effects.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/_satterthwaite.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mixed/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/_ci.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/_influence.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/montecarlo/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/_likelihood.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/_solver.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/backends/gpu_likelihood.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multinomial/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/_factor.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/_pca.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/_rotation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/multivariate/backends/gpu_pca.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_monotone.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/base.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/gpu_fp32.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/gpu_fp64.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_objectives/parameterizations.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/_utils.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/_em_batched_patterns.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/_squarem.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/datasets.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/patterns.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/mvnmle/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/_likelihood.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/_solver.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/backends/gpu_likelihood.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/ordinal/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/py.typed +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/_formatting.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/_glm.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/_linear.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/_nb_theta.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/backends/cpu_glm.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/backends/gpu_glm.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/families.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/regression/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/_cox.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/_discrete.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/_km.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/_logrank.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/backends/cpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/backends/gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/solution.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/survival/solvers.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_acf.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_batch.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_factored.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_fit.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_forecast.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_kalman.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_likelihood.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_arima_order.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_common.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_decomposition.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_differencing.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_ets_fit.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_ets_forecast.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_ets_models.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_stationarity.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/_whittle.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/backends/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/backends/whittle_batch_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/pystatistics/timeseries/backends/whittle_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_contrasts.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_design.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_factorial.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_levene.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_oneway.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_posthoc.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/anova/test_repeated_measures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/benchmark_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/core/test_datasource.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/core/test_exceptions.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/core/test_result.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/core/test_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_cor.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_cov.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_describe.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_missing.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_moments.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_quantile.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/descriptive/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_ancova_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_ancova_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_bonferroni_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_bonferroni_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_eta_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_eta_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_levene_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_levene_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_oneway_balanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_oneway_balanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_oneway_unbalanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_oneway_unbalanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_rm_mixed_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_rm_mixed_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_rm_within_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_rm_within_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_tukey_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_tukey_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_twoway_balanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_twoway_balanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_twoway_unbalanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/anova_twoway_unbalanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/basic_100x3.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/basic_100x3_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/basic_100x3_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/collinear_almost.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/collinear_almost_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/collinear_almost_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_basic_100x5.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_basic_100x5_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_basic_100x5_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_constant_column.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_constant_column_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_constant_column_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_extreme_values.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_extreme_values_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_extreme_values_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_large_1000x10.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_large_1000x10_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_large_1000x10_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_columnwise.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_columnwise_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_columnwise_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_scattered.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_scattered_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_nan_scattered_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_negative_correlation.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_negative_correlation_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_negative_correlation_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_perfect_correlation.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_perfect_correlation_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_perfect_correlation_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_single_column.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_single_column_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_single_column_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_ties.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_ties_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/desc_ties_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/different_scales.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/different_scales_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/different_scales_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_anova_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_descriptive_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_glm_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_hypothesis_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_mixed_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_montecarlo_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/generate_survival_fixtures.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_balanced.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_balanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_balanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_basic.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_basic_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_basic_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_large.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_large_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_large_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_separated.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_separated_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_binomial_separated_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_basic.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_basic_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_basic_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_large.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_large_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_gaussian_large_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_basic.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_basic_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_basic_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_large_counts.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_large_counts_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_large_counts_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_zeros.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_zeros_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/glm_poisson_zeros_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/high_noise.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/high_noise_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/high_noise_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_2x2_yates_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_2x2_yates_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_3x3_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_3x3_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_gof_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_gof_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_gof_unequal_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_chisq_gof_unequal_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_2x2_less_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_2x2_less_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_2x2_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_2x2_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_3x3_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_fisher_3x3_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_ks_onesample_norm_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_ks_onesample_norm_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_ks_twosample_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_ks_twosample_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_prop_onesample_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_prop_onesample_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_prop_twosample_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_prop_twosample_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_onesample_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_onesample_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_paired_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_paired_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_pooled_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_pooled_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_welch_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_t_welch_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_var_basic_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_var_basic_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_wilcox_ranksum_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_wilcox_ranksum_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_wilcox_signed_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/htest_wilcox_signed_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/ill_conditioned.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/ill_conditioned_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/ill_conditioned_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/large_coeffs.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/large_coeffs_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/large_coeffs_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_90_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_90_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_normal_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_normal_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_skewed_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_ci_skewed_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_mean_balanced_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_mean_balanced_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_mean_ordinary_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_mean_ordinary_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_median_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_median_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_variance_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_boot_variance_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_greater_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_greater_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_not_significant_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_not_significant_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_significant_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mc_perm_significant_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/glmm_binomial.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/glmm_poisson.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/lmm_crossed.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/lmm_intercept.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/lmm_ml.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/lmm_no_effect.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/lmm_slope.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/mixed_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/mixed/mixed_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/near_square.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/near_square_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/near_square_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/no_intercept.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/no_intercept_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/no_intercept_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_anova_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_descriptive_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_glm_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_hypothesis_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_mixed_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_montecarlo_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_survival_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_r_validation.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/run_validation.sh +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/small_noise.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/small_noise_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/small_noise_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_breslow_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_breslow_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_single_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_single_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_ties_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_ties_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_two_cov_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_cox_two_cov_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_basic_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_basic_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_heavy_cens_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_heavy_cens_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_loglog_ci_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_loglog_ci_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_no_cens_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_no_cens_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_plain_ci_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_plain_ci_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_ties_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_km_ties_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_peto_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_peto_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_three_group_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_three_group_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_two_group_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/surv_lr_two_group_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/tall_skinny.csv +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/tall_skinny_meta.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/tall_skinny_r_results.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/fixtures/validate_against_r.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/gam/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_chisq_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_design_split.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_fisher_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_ks_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_p_adjust.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_prop_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_t_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_var_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/hypothesis/test_wilcox_test.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_glmm.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_lmm_crossed.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_lmm_intercept.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_lmm_nested.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_lmm_slope.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_pls.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_random_effects.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mixed/test_satterthwaite.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_batched_solver.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_boot_ci.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_bootstrap.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_influence.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_permutation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/montecarlo/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/multinomial/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/multinomial/test_multinom.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/multivariate/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/multivariate/test_multivariate.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/apple_em_reference.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/apple_reference.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/generate_em_fixtures.R +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_apple.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_complete.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_extreme.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_missvals.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_simple_mcar.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/little_mcar_summary.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/missvals_em_reference.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/missvals_reference.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/references/small_test_reference.json +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_em.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_mcar.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_mlest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_monotone.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_no_silent_fallback.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/mvnmle/test_squarem.py +0 -0
- {pystatistics-2.3.0/tests/nonparametric_mcar → pystatistics-3.0.1/tests/ordinal}/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/ordinal/test_ordinal.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/benchmark.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/benchmark.r +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_fit.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_gamma_nb.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_glm.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_glm_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_glm_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_module_split.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/regression/test_stress_gpu.py +0 -0
- {pystatistics-2.3.0/tests/ordinal → pystatistics-3.0.1/tests/survival}/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/conftest.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_coxph.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_discrete_time.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_gpu.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_kaplan_meier.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_logrank.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/survival/test_r_validation.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/test_code_quality.py +0 -0
- {pystatistics-2.3.0/tests/survival → pystatistics-3.0.1/tests/timeseries}/__init__.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/timeseries/test_acf_stationarity.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/timeseries/test_arima.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/timeseries/test_decomposition.py +0 -0
- {pystatistics-2.3.0 → pystatistics-3.0.1}/tests/timeseries/test_ets.py +0 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Release Checklist
|
|
2
|
+
|
|
3
|
+
> `release.py` only automates the version bump in `pyproject.toml` and
|
|
4
|
+
> `<package>/__init__.py`. Everything else is by hand — on purpose.
|
|
5
|
+
> The CHANGELOG entry in particular is authorship work, not mechanical:
|
|
6
|
+
> `UNRELEASED.md` is written in internal voice, `CHANGELOG.md` is public.
|
|
7
|
+
|
|
8
|
+
## Release flow
|
|
9
|
+
|
|
10
|
+
1. **Sanity check:** `python .release/release.py --status`
|
|
11
|
+
|
|
12
|
+
2. **Write the `CHANGELOG.md` entry by hand.** Open `UNRELEASED.md` for
|
|
13
|
+
reference, then translate it into user-facing prose at the top of
|
|
14
|
+
`CHANGELOG.md` under `## X.Y.Z`. Do NOT copy verbatim. Strip:
|
|
15
|
+
- internal rule numbers (e.g. "Rule 9", "Rule 10")
|
|
16
|
+
- internal project names
|
|
17
|
+
- test counts / coverage numbers / process rationale
|
|
18
|
+
- references to `CLAUDE.md` by name or implication
|
|
19
|
+
|
|
20
|
+
Keep:
|
|
21
|
+
- API additions / removals
|
|
22
|
+
- behaviour changes a user would notice
|
|
23
|
+
- bug fixes (in terms of the user-visible symptom)
|
|
24
|
+
- performance numbers (with shape context)
|
|
25
|
+
|
|
26
|
+
3. **Update `README.md` "What's New".** Same translation rules as the
|
|
27
|
+
CHANGELOG. The README should lead with library identity; the changelog
|
|
28
|
+
lives near the bottom.
|
|
29
|
+
|
|
30
|
+
4. **Bump versions:** `python .release/release.py --bump X.Y.Z` — this
|
|
31
|
+
only touches `pyproject.toml` and `<package>/__init__.py`.
|
|
32
|
+
|
|
33
|
+
5. **Review:** `git diff`
|
|
34
|
+
|
|
35
|
+
6. **Commit and tag:**
|
|
36
|
+
```
|
|
37
|
+
git add pyproject.toml <package>/__init__.py CHANGELOG.md README.md
|
|
38
|
+
git commit -m "Release X.Y.Z"
|
|
39
|
+
git tag vX.Y.Z
|
|
40
|
+
git push origin <branch>
|
|
41
|
+
git push origin vX.Y.Z
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
7. **Reset UNRELEASED.md** — only now, after the release commit is in:
|
|
45
|
+
```
|
|
46
|
+
python .release/release.py --reset-unreleased
|
|
47
|
+
git add .release/UNRELEASED.md
|
|
48
|
+
git commit -m "Reset UNRELEASED.md for next cycle"
|
|
49
|
+
git push
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
8. **Publish to PyPI via GitHub release:**
|
|
53
|
+
```
|
|
54
|
+
gh release create vX.Y.Z --title 'vX.Y.Z' --notes-file CHANGELOG.md
|
|
55
|
+
```
|
|
56
|
+
This triggers `publish.yml` → PyPI.
|
|
57
|
+
|
|
58
|
+
9. **Verify:** `pip install <package>==X.Y.Z`
|
|
59
|
+
|
|
60
|
+
## Why there is no `--commit` mode
|
|
61
|
+
|
|
62
|
+
A previous version of this script had a `--commit` flag that did the
|
|
63
|
+
changelog prepend, the UNRELEASED reset, and the git commit/tag/push in
|
|
64
|
+
one shot. Two problems:
|
|
65
|
+
|
|
66
|
+
- It copied `UNRELEASED.md` verbatim into `CHANGELOG.md`. `UNRELEASED.md`
|
|
67
|
+
is written in internal developer voice; `CHANGELOG.md` is read by
|
|
68
|
+
strangers on PyPI. That's a Rule-10 audience violation, and the
|
|
69
|
+
automation made it the path of least resistance.
|
|
70
|
+
- It coupled the mechanical bump with the human authorship steps, so
|
|
71
|
+
there was no natural point to review the translation before the
|
|
72
|
+
commit went out.
|
|
73
|
+
|
|
74
|
+
Doing the authorship by hand, then using the script only for the literal
|
|
75
|
+
version strings, is slower on paper and safer in practice.
|
|
76
|
+
|
|
77
|
+
## Historical foot-guns
|
|
78
|
+
|
|
79
|
+
- **v1.6.1 (2026-ish):** release commit missed unstaged source edits
|
|
80
|
+
because only tracked files were staged. Mitigation: always run
|
|
81
|
+
`git status` before `git commit` and confirm the diff contains every
|
|
82
|
+
file the release should ship.
|
|
83
|
+
- **Multiple PyPI publish failures:** every one has been a missed manual
|
|
84
|
+
step from this list. Work top to bottom; do not skip.
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Release utility for pystatistics / pystatsbio.
|
|
4
|
+
|
|
5
|
+
This script ONLY automates the mechanical, judgement-free parts of a
|
|
6
|
+
release. Everything that needs human authorship — in particular, the
|
|
7
|
+
public-facing CHANGELOG.md entry — is left to you.
|
|
8
|
+
|
|
9
|
+
Why: UNRELEASED.md is written in internal developer voice (rule numbers,
|
|
10
|
+
project names, test counts, development rationale). CHANGELOG.md is a
|
|
11
|
+
public file read by strangers on PyPI. Copying one into the other is a
|
|
12
|
+
Rule-10 violation. The translation must be done by hand.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python .release/release.py --status # show version state + unreleased summary
|
|
16
|
+
python .release/release.py --check X.Y.Z # dry-run: validate the bump
|
|
17
|
+
python .release/release.py --bump X.Y.Z # bump pyproject.toml + __init__.py
|
|
18
|
+
python .release/release.py --reset-unreleased # wipe UNRELEASED.md to template
|
|
19
|
+
(only after CHANGELOG entry is written)
|
|
20
|
+
|
|
21
|
+
What this script does:
|
|
22
|
+
--bump:
|
|
23
|
+
1. Validates new version > current version
|
|
24
|
+
2. Writes new version to pyproject.toml
|
|
25
|
+
3. Writes new version to <package>/__init__.py
|
|
26
|
+
|
|
27
|
+
--reset-unreleased:
|
|
28
|
+
1. Overwrites UNRELEASED.md with the empty template
|
|
29
|
+
|
|
30
|
+
What this script does NOT do (you must do these yourself):
|
|
31
|
+
- Write the CHANGELOG.md entry. Translate UNRELEASED.md into
|
|
32
|
+
user-facing prose by hand. Do not copy verbatim.
|
|
33
|
+
- Update the README "What's New" section.
|
|
34
|
+
- git add / commit / tag / push.
|
|
35
|
+
- gh release create.
|
|
36
|
+
|
|
37
|
+
Typical flow:
|
|
38
|
+
1. `--status` (sanity check)
|
|
39
|
+
2. Write the CHANGELOG.md entry by hand. (translation)
|
|
40
|
+
3. Update README.md "What's New" by hand. (translation)
|
|
41
|
+
4. `--bump X.Y.Z` (mechanical)
|
|
42
|
+
5. `git diff` (review)
|
|
43
|
+
6. git add / commit / tag / push (manual)
|
|
44
|
+
7. `--reset-unreleased` (only after commit)
|
|
45
|
+
8. Commit the reset, then `gh release create`.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
from __future__ import annotations
|
|
49
|
+
|
|
50
|
+
import re
|
|
51
|
+
import sys
|
|
52
|
+
import textwrap
|
|
53
|
+
from pathlib import Path
|
|
54
|
+
|
|
55
|
+
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
56
|
+
RELEASE_DIR = REPO_ROOT / ".release"
|
|
57
|
+
UNRELEASED_PATH = RELEASE_DIR / "UNRELEASED.md"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Discovery
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
def find_package() -> str:
|
|
65
|
+
"""Find the Python package directory (has __init__.py with __version__)."""
|
|
66
|
+
for d in sorted(REPO_ROOT.iterdir()):
|
|
67
|
+
if d.is_dir() and (d / "__init__.py").exists():
|
|
68
|
+
init_text = (d / "__init__.py").read_text()
|
|
69
|
+
if "__version__" in init_text:
|
|
70
|
+
return d.name
|
|
71
|
+
raise FileNotFoundError("Cannot find package with __version__ in __init__.py")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_current_version() -> str:
|
|
75
|
+
toml = (REPO_ROOT / "pyproject.toml").read_text()
|
|
76
|
+
m = re.search(r'^version\s*=\s*"([^"]+)"', toml, re.MULTILINE)
|
|
77
|
+
if not m:
|
|
78
|
+
raise ValueError("Cannot find version in pyproject.toml")
|
|
79
|
+
return m.group(1)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def get_init_version(package: str) -> str:
|
|
83
|
+
init = (REPO_ROOT / package / "__init__.py").read_text()
|
|
84
|
+
m = re.search(r'__version__\s*=\s*"([^"]+)"', init)
|
|
85
|
+
if not m:
|
|
86
|
+
raise ValueError(f"Cannot find __version__ in {package}/__init__.py")
|
|
87
|
+
return m.group(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_changelog_version() -> str | None:
|
|
91
|
+
changelog = REPO_ROOT / "CHANGELOG.md"
|
|
92
|
+
if not changelog.exists():
|
|
93
|
+
return None
|
|
94
|
+
text = changelog.read_text()
|
|
95
|
+
m = re.search(r'^## (\d+\.\d+\.\d+)', text, re.MULTILINE)
|
|
96
|
+
return m.group(1) if m else None
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------------------------
|
|
100
|
+
# UNRELEASED.md
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
EMPTY_UNRELEASED = textwrap.dedent("""\
|
|
104
|
+
# Unreleased Changes
|
|
105
|
+
|
|
106
|
+
> This file tracks all changes since the last stable release.
|
|
107
|
+
> Updated by whoever makes a change, on whatever machine.
|
|
108
|
+
> Synced via git so all sessions (Mac, Linux, etc.) see the same state.
|
|
109
|
+
>
|
|
110
|
+
> When ready to release, run: `python .release/release.py --status`
|
|
111
|
+
> and follow the manual release flow in the script docstring.
|
|
112
|
+
|
|
113
|
+
## Changes
|
|
114
|
+
|
|
115
|
+
*(empty — no unreleased changes yet)*
|
|
116
|
+
""")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_unreleased_content() -> str:
|
|
120
|
+
if not UNRELEASED_PATH.exists():
|
|
121
|
+
return ""
|
|
122
|
+
text = UNRELEASED_PATH.read_text()
|
|
123
|
+
m = re.search(r'^## Changes\s*\n(.*)\Z', text, re.MULTILINE | re.DOTALL)
|
|
124
|
+
if not m:
|
|
125
|
+
return ""
|
|
126
|
+
content = m.group(1).strip()
|
|
127
|
+
if not content or content == "*(empty — no unreleased changes yet)*":
|
|
128
|
+
return ""
|
|
129
|
+
return content
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def reset_unreleased() -> None:
|
|
133
|
+
UNRELEASED_PATH.write_text(EMPTY_UNRELEASED)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Version bumping
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
def bump_pyproject(new_version: str) -> None:
|
|
141
|
+
path = REPO_ROOT / "pyproject.toml"
|
|
142
|
+
text = path.read_text()
|
|
143
|
+
text = re.sub(
|
|
144
|
+
r'^(version\s*=\s*)"[^"]+"',
|
|
145
|
+
rf'\g<1>"{new_version}"',
|
|
146
|
+
text,
|
|
147
|
+
count=1,
|
|
148
|
+
flags=re.MULTILINE,
|
|
149
|
+
)
|
|
150
|
+
path.write_text(text)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def bump_init(package: str, new_version: str) -> None:
|
|
154
|
+
path = REPO_ROOT / package / "__init__.py"
|
|
155
|
+
text = path.read_text()
|
|
156
|
+
text = re.sub(
|
|
157
|
+
r'(__version__\s*=\s*)"[^"]+"',
|
|
158
|
+
rf'\g<1>"{new_version}"',
|
|
159
|
+
text,
|
|
160
|
+
count=1,
|
|
161
|
+
)
|
|
162
|
+
path.write_text(text)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def parse_version(v: str) -> tuple[int, ...]:
|
|
166
|
+
return tuple(int(x) for x in v.split("."))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def validate_version(new: str, current: str) -> None:
|
|
170
|
+
try:
|
|
171
|
+
new_t = parse_version(new)
|
|
172
|
+
cur_t = parse_version(current)
|
|
173
|
+
except (ValueError, AttributeError) as e:
|
|
174
|
+
raise ValueError(f"Invalid version format: {e}") from e
|
|
175
|
+
|
|
176
|
+
if new_t <= cur_t:
|
|
177
|
+
raise ValueError(
|
|
178
|
+
f"New version {new} must be greater than current {current}"
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
# ---------------------------------------------------------------------------
|
|
183
|
+
# Commands
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
def cmd_status() -> None:
|
|
187
|
+
package = find_package()
|
|
188
|
+
v_toml = get_current_version()
|
|
189
|
+
v_init = get_init_version(package)
|
|
190
|
+
v_log = get_changelog_version()
|
|
191
|
+
unreleased = get_unreleased_content()
|
|
192
|
+
|
|
193
|
+
print(f"Package: {package}")
|
|
194
|
+
print(f"pyproject: {v_toml}")
|
|
195
|
+
print(f"__init__: {v_init}")
|
|
196
|
+
print(f"CHANGELOG: {v_log or '(none)'}")
|
|
197
|
+
|
|
198
|
+
if v_toml != v_init:
|
|
199
|
+
print(f"\n WARNING: pyproject ({v_toml}) != __init__ ({v_init})")
|
|
200
|
+
if v_log and v_toml != v_log:
|
|
201
|
+
print(f"\n WARNING: pyproject ({v_toml}) != CHANGELOG ({v_log})")
|
|
202
|
+
|
|
203
|
+
print("\nUnreleased changes (internal voice — do NOT paste into CHANGELOG.md):")
|
|
204
|
+
if unreleased:
|
|
205
|
+
lines = unreleased.split("\n")
|
|
206
|
+
for line in lines[:12]:
|
|
207
|
+
print(f" {line}")
|
|
208
|
+
if len(lines) > 12:
|
|
209
|
+
print(f" ... ({len(lines) - 12} more lines)")
|
|
210
|
+
else:
|
|
211
|
+
print(" (none)")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def cmd_check(new_version: str) -> None:
|
|
215
|
+
package = find_package()
|
|
216
|
+
current = get_current_version()
|
|
217
|
+
validate_version(new_version, current)
|
|
218
|
+
|
|
219
|
+
print(f"Package: {package}")
|
|
220
|
+
print(f"Current: {current}")
|
|
221
|
+
print(f"New: {new_version}")
|
|
222
|
+
print("\nWould update (only these two files):")
|
|
223
|
+
print(f" pyproject.toml: {current} → {new_version}")
|
|
224
|
+
print(f" {package}/__init__.py: {current} → {new_version}")
|
|
225
|
+
print("\nThis script will NOT touch CHANGELOG.md, README.md, UNRELEASED.md, or git.")
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def cmd_bump(new_version: str) -> None:
|
|
229
|
+
package = find_package()
|
|
230
|
+
current = get_current_version()
|
|
231
|
+
validate_version(new_version, current)
|
|
232
|
+
|
|
233
|
+
print(f"Bumping {package} {current} → {new_version}\n")
|
|
234
|
+
|
|
235
|
+
bump_pyproject(new_version)
|
|
236
|
+
print(f" ✓ pyproject.toml → {new_version}")
|
|
237
|
+
|
|
238
|
+
bump_init(package, new_version)
|
|
239
|
+
print(f" ✓ {package}/__init__.py → {new_version}")
|
|
240
|
+
|
|
241
|
+
print(f"\n{'='*50}")
|
|
242
|
+
print(f" Version bumped to {new_version}")
|
|
243
|
+
print(f"{'='*50}")
|
|
244
|
+
print("\nRemaining steps (all manual):")
|
|
245
|
+
print(" 1. Verify CHANGELOG.md has a hand-written entry for this version")
|
|
246
|
+
print(" (do NOT paste UNRELEASED.md verbatim — translate to user-facing prose).")
|
|
247
|
+
print(" 2. Verify README.md 'What's New' reflects the same user-facing summary.")
|
|
248
|
+
print(" 3. git diff # review")
|
|
249
|
+
print(f" 4. git add pyproject.toml {package}/__init__.py CHANGELOG.md README.md")
|
|
250
|
+
print(f" 5. git commit -m 'Release {new_version}'")
|
|
251
|
+
print(f" 6. git tag v{new_version}")
|
|
252
|
+
print(f" 7. git push origin <branch> && git push origin v{new_version}")
|
|
253
|
+
print(" 8. python .release/release.py --reset-unreleased")
|
|
254
|
+
print(" git add .release/UNRELEASED.md && git commit -m 'Reset UNRELEASED.md'")
|
|
255
|
+
print(" git push")
|
|
256
|
+
print(f" 9. gh release create v{new_version} --title 'v{new_version}' --notes-file CHANGELOG.md")
|
|
257
|
+
print(f" 10. Verify: pip install {package}=={new_version}")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def cmd_reset_unreleased() -> None:
|
|
261
|
+
reset_unreleased()
|
|
262
|
+
print(f" ✓ {UNRELEASED_PATH.relative_to(REPO_ROOT)} reset to empty template")
|
|
263
|
+
print("\nRemember to commit the reset:")
|
|
264
|
+
print(" git add .release/UNRELEASED.md")
|
|
265
|
+
print(" git commit -m 'Reset UNRELEASED.md for next cycle'")
|
|
266
|
+
print(" git push")
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
# ---------------------------------------------------------------------------
|
|
270
|
+
# Main
|
|
271
|
+
# ---------------------------------------------------------------------------
|
|
272
|
+
|
|
273
|
+
USAGE = """\
|
|
274
|
+
Usage:
|
|
275
|
+
python .release/release.py --status # show version state
|
|
276
|
+
python .release/release.py --check X.Y.Z # dry-run: validate the bump
|
|
277
|
+
python .release/release.py --bump X.Y.Z # bump pyproject.toml + __init__.py
|
|
278
|
+
python .release/release.py --reset-unreleased # wipe UNRELEASED.md to template
|
|
279
|
+
|
|
280
|
+
See the module docstring for the full manual release flow.
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def main() -> None:
|
|
285
|
+
if len(sys.argv) < 2:
|
|
286
|
+
print(USAGE)
|
|
287
|
+
sys.exit(1)
|
|
288
|
+
|
|
289
|
+
arg = sys.argv[1]
|
|
290
|
+
|
|
291
|
+
if arg == "--status":
|
|
292
|
+
cmd_status()
|
|
293
|
+
elif arg == "--check":
|
|
294
|
+
if len(sys.argv) < 3:
|
|
295
|
+
print("Usage: python .release/release.py --check X.Y.Z")
|
|
296
|
+
sys.exit(1)
|
|
297
|
+
cmd_check(sys.argv[2])
|
|
298
|
+
elif arg == "--bump":
|
|
299
|
+
if len(sys.argv) < 3:
|
|
300
|
+
print("Usage: python .release/release.py --bump X.Y.Z")
|
|
301
|
+
sys.exit(1)
|
|
302
|
+
cmd_bump(sys.argv[2])
|
|
303
|
+
elif arg == "--reset-unreleased":
|
|
304
|
+
cmd_reset_unreleased()
|
|
305
|
+
else:
|
|
306
|
+
print(f"Unknown command: {arg}\n")
|
|
307
|
+
print(USAGE)
|
|
308
|
+
sys.exit(1)
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
if __name__ == "__main__":
|
|
312
|
+
main()
|
|
@@ -1,5 +1,182 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
|
|
5
|
+
Metadata and documentation polish. No API changes.
|
|
6
|
+
|
|
7
|
+
- **Development Status classifier updated.** `pyproject.toml` now
|
|
8
|
+
declares `Development Status :: 5 - Production/Stable` (previously
|
|
9
|
+
`3 - Alpha`). This only affects the PyPI classifier — no runtime
|
|
10
|
+
behaviour changes.
|
|
11
|
+
|
|
12
|
+
- **Stale optional-dependency extra removed.** The
|
|
13
|
+
`[nonparametric_mcar]` extra has been removed from `pyproject.toml`.
|
|
14
|
+
The subpackage it referenced was removed in 3.0.0, but the extra had
|
|
15
|
+
remained listed on PyPI. `pip install pystatistics[nonparametric_mcar]`
|
|
16
|
+
will now fail with an "extra not provided" message instead of
|
|
17
|
+
silently installing a dependency for a subpackage that does not exist.
|
|
18
|
+
|
|
19
|
+
- **README restructured.** The README now leads with the library's
|
|
20
|
+
design philosophy and module table, with the changelog near the
|
|
21
|
+
bottom. A first-time reader on PyPI sees what PyStatistics is before
|
|
22
|
+
seeing what has changed release-to-release.
|
|
23
|
+
|
|
24
|
+
## 3.0.0
|
|
25
|
+
|
|
26
|
+
### BREAKING: Removed MoM MCAR test and the entire `nonparametric_mcar` subpackage
|
|
27
|
+
|
|
28
|
+
`pystatistics.mvnmle.mom_mcar_test` and the full
|
|
29
|
+
`pystatistics.nonparametric_mcar` subpackage (propensity / HSIC /
|
|
30
|
+
MissMech + their shared `NonparametricMCARResult`) have been removed.
|
|
31
|
+
These were added in 2.2.0 and 2.3.0 respectively, specifically in
|
|
32
|
+
service of Lacuna's missingness-mechanism cache, NOT as general-
|
|
33
|
+
purpose statistical methods. That was a scope-boundary mistake on
|
|
34
|
+
pystatistics's part: we're a general-purpose statistics library, and
|
|
35
|
+
these tests are project-specific feature-extraction helpers.
|
|
36
|
+
|
|
37
|
+
**What remains in pystatistics:**
|
|
38
|
+
- `pystatistics.mvnmle.little_mcar_test` — the canonical Little (1988)
|
|
39
|
+
MLE-plug-in test. Textbook, general-purpose, stays.
|
|
40
|
+
- `MCARTestResult` dataclass — retained (downstream packages that
|
|
41
|
+
implement their own MCAR variants can and do reuse it).
|
|
42
|
+
- All of `mlest`, the EM / SQUAREM machinery, `analyze_patterns`,
|
|
43
|
+
`PatternInfo`, etc. — unchanged.
|
|
44
|
+
|
|
45
|
+
**Where the removed tests live now:** the cache-scale variants have
|
|
46
|
+
moved into Lacuna as `lacuna.analysis.mcar`:
|
|
47
|
+
|
|
48
|
+
- `lacuna.analysis.mcar.mom_mcar_test` (method-of-moments plug-in)
|
|
49
|
+
- `lacuna.analysis.mcar.propensity_mcar_test` (RF/GBM + analytical or
|
|
50
|
+
permutation null)
|
|
51
|
+
- `lacuna.analysis.mcar.hsic_mcar_test` (Gretton gamma or permutation null)
|
|
52
|
+
- `lacuna.analysis.mcar.missmech_mcar_test` (Jamshidian-Jalal-style)
|
|
53
|
+
- `lacuna.analysis.mcar.NonparametricMCARResult`
|
|
54
|
+
|
|
55
|
+
All the 2.3.0/2.4.0 performance work that was in-flight in this file
|
|
56
|
+
(analytical AUC p-value for propensity, HGB default, Gretton gamma
|
|
57
|
+
null for HSIC, vectorised MissMech permutation, etc.) moved with the
|
|
58
|
+
code. Downstream: Lacuna's cache build is unaffected — it now imports
|
|
59
|
+
from `lacuna.analysis.mcar` instead of `pystatistics.nonparametric_mcar`.
|
|
60
|
+
|
|
61
|
+
**Migration for external users (if any):**
|
|
62
|
+
- `from pystatistics.mvnmle import mom_mcar_test` →
|
|
63
|
+
`from lacuna.analysis.mcar import mom_mcar_test` (if using Lacuna).
|
|
64
|
+
Alternatively, the MoM algorithm is ~100 LOC of pairwise-deletion
|
|
65
|
+
moments + per-pattern chi-square and is straightforward to inline
|
|
66
|
+
if you don't want a Lacuna dependency.
|
|
67
|
+
- `from pystatistics.nonparametric_mcar import *` →
|
|
68
|
+
`from lacuna.analysis.mcar import *`.
|
|
69
|
+
|
|
70
|
+
**Why a major bump (3.0.0):** public API surface shrunk; imports that
|
|
71
|
+
worked in 2.x now fail. Even though our active user population is
|
|
72
|
+
small (internal), the convention matters — breaking removals are
|
|
73
|
+
semver-major.
|
|
74
|
+
|
|
75
|
+
**Dropped files:**
|
|
76
|
+
- `pystatistics/nonparametric_mcar/` (subpackage, 5 files)
|
|
77
|
+
- `pystatistics/mvnmle/mcar_test.py` lost `mom_mcar_test`,
|
|
78
|
+
`_resolve_mom_backend`, `_pairwise_deletion_moments`, and the
|
|
79
|
+
`_MOM_GPU_WORTH_IT_THRESHOLD` constant. `little_mcar_test` and
|
|
80
|
+
`MCARTestResult` remain.
|
|
81
|
+
- `pyproject.toml` lost the `nonparametric_mcar` optional extra.
|
|
82
|
+
- `tests/mvnmle/test_mom_mcar.py` and `tests/nonparametric_mcar/`.
|
|
83
|
+
|
|
84
|
+
(The dead batched MCAR chi-square machinery that this removal left
|
|
85
|
+
stranded has also been cleaned up — see the "Dead-code cleanup"
|
|
86
|
+
section below.)
|
|
87
|
+
|
|
88
|
+
### Fixed flaky GAM GPU FP64 `total_edf` test
|
|
89
|
+
|
|
90
|
+
`tests/gam/test_gam.py::TestGAMGPU::test_gpu_fp64_matches_cpu_fitted_and_gcv`
|
|
91
|
+
had been flaking intermittently since 1.8.0. Measured CPU/GPU drift on
|
|
92
|
+
the `sine_data` fixture:
|
|
93
|
+
|
|
94
|
+
- `fitted_values` max abs diff: ~7e-8 (machine precision)
|
|
95
|
+
- `deviance` relative diff: ~1e-7
|
|
96
|
+
- `gcv` relative diff: ~3e-5
|
|
97
|
+
- `total_edf` relative diff: **~1.4e-3** (narrowly failing the 1e-3 bar)
|
|
98
|
+
|
|
99
|
+
Diagnosis: the primary fit statistics (fitted_values, deviance, GCV)
|
|
100
|
+
sit at the GCV-minimising λ, i.e. at a local minimum of `GCV(λ)`, so
|
|
101
|
+
they are insensitive to the tiny λ-drift the L-BFGS-B outer search
|
|
102
|
+
lands on across CPU/GPU. `total_edf`, however, passes through a trace
|
|
103
|
+
of `(X'WX + λ·S)⁻¹·X'WX`, which is LINEAR in λ near the optimum. With
|
|
104
|
+
the penalised normal-equation matrix at cond ≈ 1e16-17 the EDF trace
|
|
105
|
+
moves O(10⁻³) per O(10⁻⁶) λ shift — structurally more sensitive than
|
|
106
|
+
the fit statistics that a 1e-4 tolerance gated.
|
|
107
|
+
|
|
108
|
+
Fix: tolerance widened from `rel=1e-3` to `rel=5e-3` on `total_edf`
|
|
109
|
+
only. The other three assertions (fitted_values, deviance, GCV) keep
|
|
110
|
+
their 1e-4 margin. Test comment updated with the λ-sensitivity
|
|
111
|
+
analysis so future maintainers don't re-tighten it without
|
|
112
|
+
understanding why it's at 5e-3.
|
|
113
|
+
|
|
114
|
+
### Fixed GAM GPU smooth-term chi-squared bug (null-space absorption)
|
|
115
|
+
|
|
116
|
+
Surfaced while tightening the `total_edf` tolerance above: the chi-
|
|
117
|
+
squared statistic reported in `smooth_terms` was diverging ~8×
|
|
118
|
+
between CPU and GPU backends — CPU ≈ 19, GPU ≈ 156 on the
|
|
119
|
+
`sine_data` fixture — despite fitted values agreeing to FP64
|
|
120
|
+
precision. This was a latent correctness bug in the GPU backend
|
|
121
|
+
going back to whenever the GAM GPU path was first written.
|
|
122
|
+
|
|
123
|
+
Root cause: the penalised normal matrix `A = X'WX + Σ λⱼ Sⱼ` has
|
|
124
|
+
condition number up to ~1e17 when λ is small — the penalty does not
|
|
125
|
+
fully eliminate the design matrix's null space (the constant / linear
|
|
126
|
+
directions in the spline basis's null space). On such `A`,
|
|
127
|
+
`torch.linalg.solve` (LU on device) and `np.linalg.cholesky`
|
|
128
|
+
(CPU reference path) converge to the same `X·β` (fitted values) but
|
|
129
|
+
pick DIFFERENT null-space-representative `β` (coefficients).
|
|
130
|
+
Concretely on `sine_data`: fitted values agreed to 7e-8 but
|
|
131
|
+
coefficients were shifted by a constant ±1.73 between the parametric
|
|
132
|
+
intercept and every smooth basis coefficient — exactly the null-space
|
|
133
|
+
direction the penalty fails to constrain. Since `smooth_terms.chi_sq`
|
|
134
|
+
is computed directly from `β` (via `β_j'·β_j / scale`), the null-
|
|
135
|
+
space shift broke it despite the rest of the fit being numerically
|
|
136
|
+
sound.
|
|
137
|
+
|
|
138
|
+
Fix: the GPU backend now canonicalises the final `β` by re-solving
|
|
139
|
+
`A·β = b` via numpy's Cholesky-with-LU-fallback — the same logic
|
|
140
|
+
the CPU path uses in `_pirls_step`. This lives in a new
|
|
141
|
+
`_canonicalise_beta` method; `fit_fixed` and `edf_per_term` both call
|
|
142
|
+
it after their P-IRLS loops converge. In-loop P-IRLS still uses
|
|
143
|
+
torch's fast LU on device (null-space ambiguity doesn't matter
|
|
144
|
+
there — only the resulting `μ` drives convergence). The D2H
|
|
145
|
+
round-trip for the final solve is ~100 µs on a p×p matrix with p
|
|
146
|
+
typically ≤ 80; the dominant GPU work (the n×p GEMM in each P-IRLS
|
|
147
|
+
step) stays on device.
|
|
148
|
+
|
|
149
|
+
Test: `TestGAMGPU::test_gpu_fp64_matches_cpu_fitted_and_gcv` gained
|
|
150
|
+
coefficient-level assertions (`rtol=1e-3, atol=1e-4`) and chi-squared
|
|
151
|
+
agreement (`rel=1e-3`) so this regression is caught if it returns.
|
|
152
|
+
Measured post-fix: coef max abs diff drops from 1.73 to 9e-5;
|
|
153
|
+
chi_sq rel diff drops from ~8× to 1.6e-5.
|
|
154
|
+
|
|
155
|
+
### Dead-code cleanup
|
|
156
|
+
|
|
157
|
+
While removing ``mom_mcar_test``, noticed that the batched MCAR
|
|
158
|
+
chi-square machinery (``chi_square_mcar_batched_np`` and
|
|
159
|
+
``chi_square_mcar_batched_torch`` in ``backends/_em_batched_{np,torch}.py``,
|
|
160
|
+
re-exported via the shim at ``backends/_em_batched.py``) was only ever
|
|
161
|
+
called from ``mom_mcar_test``. Those functions are now gone; shim
|
|
162
|
+
dropped their re-exports; backend file docstrings updated to record
|
|
163
|
+
the removal. Net: `_em_batched_np.py` went from 416 → 300 lines and
|
|
164
|
+
`_em_batched_torch.py` from 381 → 271 lines, and `backends/_em_batched.py`
|
|
165
|
+
lost two of its eleven shim exports.
|
|
166
|
+
|
|
167
|
+
### Process change
|
|
168
|
+
|
|
169
|
+
Added **Rule 9 "Cross-Project Scope Boundary"** to
|
|
170
|
+
`CLAUDE.md`. Future Claude Code sessions working on pystatistics must
|
|
171
|
+
not modify sibling projects (Lacuna, pystatsbio, etc.) without
|
|
172
|
+
explicit per-session user authorisation. Functionality needed by
|
|
173
|
+
another project belongs in that project until demonstrably general.
|
|
174
|
+
This removal is the retroactive correction of previous boundary
|
|
175
|
+
crossings. The same rule was added to Lacuna's and pystatsbio's
|
|
176
|
+
CLAUDE.md files (§8 and §9 respectively, numbered by where the rule
|
|
177
|
+
fit in each file's existing structure).
|
|
178
|
+
|
|
179
|
+
|
|
3
180
|
## 2.3.0
|
|
4
181
|
|
|
5
182
|
- **New subpackage `pystatistics.nonparametric_mcar`** for distribution-
|
|
@@ -218,6 +218,93 @@ file, that's a process failure.
|
|
|
218
218
|
|
|
219
219
|
---
|
|
220
220
|
|
|
221
|
+
## 9. Cross-Project Scope Boundary — Stay in the Project You Were Invited Into
|
|
222
|
+
|
|
223
|
+
A Claude Code session is invoked on one project. It does not have a licence to edit
|
|
224
|
+
any other project visible on disk. Seeing sibling repos in `/mnt/projects/` does not
|
|
225
|
+
constitute an invitation to modify them.
|
|
226
|
+
|
|
227
|
+
**Rules:**
|
|
228
|
+
- Never modify source, tests, configs, or documentation in a sibling project (any
|
|
229
|
+
repo outside the current one) on your own initiative.
|
|
230
|
+
- When you believe a change belongs in a sibling project — "this belongs upstream",
|
|
231
|
+
"this helper is more general than the current project", "I should fix a bug in
|
|
232
|
+
their API" — stop and raise it with the user. Describe what you'd change, where,
|
|
233
|
+
and why. Wait for explicit authorisation before editing the sibling repo.
|
|
234
|
+
- "Explicit authorisation" means the user said, in the current session, something
|
|
235
|
+
like "yes, also modify project X". It is NOT a precedent from a prior session. It
|
|
236
|
+
is NOT "the user probably wouldn't mind". It is NOT inferred from the fact that
|
|
237
|
+
the user depends on project X.
|
|
238
|
+
|
|
239
|
+
**Corollary — Scope Creep Is a Design Decision, Not a Technical One:**
|
|
240
|
+
Pushing project-A-specific functionality into general-purpose project B because it's
|
|
241
|
+
technically convenient dilutes project B's identity, breaks the expectations of other
|
|
242
|
+
downstream consumers, and forces project B to carry maintenance burden for a use case
|
|
243
|
+
it was never meant to serve. "Project A needs X, and X involves statistics, therefore
|
|
244
|
+
X belongs in pystatistics" is not a valid reasoning chain. X belongs in project A
|
|
245
|
+
until (a) multiple sibling projects demonstrably need it AND (b) a decision has been
|
|
246
|
+
made to promote it — either into an existing general-purpose library or into a new
|
|
247
|
+
dedicated sibling.
|
|
248
|
+
|
|
249
|
+
**What to do when you think a sibling needs a change:**
|
|
250
|
+
1. Implement the helper inside your OWN project first. It lives there until proven
|
|
251
|
+
general.
|
|
252
|
+
2. Surface the "belongs upstream" thought to the user explicitly. Propose the
|
|
253
|
+
migration as a separate task. Do not pre-emptively modify the sibling repo.
|
|
254
|
+
3. If the user authorises the migration, treat it as its own first-class task with
|
|
255
|
+
its own review, tests, and release cadence — not as a side-effect of the
|
|
256
|
+
originating feature.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 10. Audience Awareness for Public-Facing Files
|
|
261
|
+
|
|
262
|
+
Before modifying any public-facing file (`README.md`, `pyproject.toml`,
|
|
263
|
+
user-facing documentation, the PyPI description), explicitly identify the
|
|
264
|
+
audience: **a stranger on the internet who has never seen this codebase.**
|
|
265
|
+
That person has no knowledge of `CLAUDE.md`, internal project names,
|
|
266
|
+
development rationale, rule numbers, or implementation history. They need
|
|
267
|
+
to know what the library does, how to install it, and how to use it —
|
|
268
|
+
nothing else.
|
|
269
|
+
|
|
270
|
+
**The test:** *would a first-time user finding this on PyPI find this
|
|
271
|
+
useful or confusing?* If confusing, omit it.
|
|
272
|
+
|
|
273
|
+
**Rules (all specific instances of the principle above):**
|
|
274
|
+
|
|
275
|
+
- **Changelog entries must not reference internal development artifacts.**
|
|
276
|
+
Entries in `README.md`, `CHANGELOG.md`, or any public-facing document must
|
|
277
|
+
never reference `CLAUDE.md`, internal rule numbers, internal project
|
|
278
|
+
names, development process, or test counts. Acceptable content:
|
|
279
|
+
user-facing behaviour changes, API additions/removals, bug fixes, and
|
|
280
|
+
performance numbers.
|
|
281
|
+
|
|
282
|
+
Example of what must **not** appear in a public changelog:
|
|
283
|
+
> "a new Cross-Project Scope Boundary rule (Rule 9) has been added to
|
|
284
|
+
> CLAUDE.md"
|
|
285
|
+
|
|
286
|
+
- **Do not document `CLAUDE.md` changes in `README.md`.** When updating
|
|
287
|
+
`CLAUDE.md`, do not document those changes in `README.md` or any other
|
|
288
|
+
public-facing file. `CLAUDE.md` is internal developer guidance; `README.md`
|
|
289
|
+
reflects the public API, installation, and usage. `README.md` must never
|
|
290
|
+
reference `CLAUDE.md` by name or by implication.
|
|
291
|
+
|
|
292
|
+
- **`README.md` is a user document, not a changelog mirror.** When updating
|
|
293
|
+
`README.md` from `CHANGELOG.md` or release notes, do not copy entries
|
|
294
|
+
verbatim or structurally. Translate them: extract only user-facing
|
|
295
|
+
behaviour changes, rewrite in present tense describing the library's
|
|
296
|
+
current state, and omit internal process details (references to
|
|
297
|
+
`CLAUDE.md`, rule numbers, internal project names, test counts,
|
|
298
|
+
development rationale). A user reading the README should understand what
|
|
299
|
+
the library does today — not the history of how it got there.
|
|
300
|
+
|
|
301
|
+
**Corollary — The Internal Audience Already Has CLAUDE.md:**
|
|
302
|
+
Rationale that belongs in `CLAUDE.md` does not need to be duplicated into
|
|
303
|
+
public-facing files to "motivate" a change for users. Users do not need to
|
|
304
|
+
be sold on the rule behind the change — only told what changed.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
221
308
|
## Meta-Rule
|
|
222
309
|
|
|
223
310
|
If any of these rules feel inconvenient, that feeling is the point. These rules exist
|