pystatistics 2.0.1__tar.gz → 2.2.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.
Files changed (544) hide show
  1. {pystatistics-2.0.1 → pystatistics-2.2.0}/CHANGELOG.md +277 -0
  2. {pystatistics-2.0.1 → pystatistics-2.2.0}/PKG-INFO +171 -1
  3. {pystatistics-2.0.1 → pystatistics-2.2.0}/README.md +170 -0
  4. pystatistics-2.2.0/benchmarks/mvnmle_bench.py +194 -0
  5. {pystatistics-2.0.1 → pystatistics-2.2.0}/pyproject.toml +1 -1
  6. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/__init__.py +1 -1
  7. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/__init__.py +14 -1
  8. pystatistics-2.2.0/pystatistics/mvnmle/_monotone.py +230 -0
  9. pystatistics-2.2.0/pystatistics/mvnmle/backends/_em_batched.py +897 -0
  10. pystatistics-2.2.0/pystatistics/mvnmle/backends/_squarem.py +235 -0
  11. pystatistics-2.2.0/pystatistics/mvnmle/backends/em.py +517 -0
  12. pystatistics-2.2.0/pystatistics/mvnmle/mcar_test.py +664 -0
  13. pystatistics-2.2.0/pystatistics/mvnmle/solvers.py +344 -0
  14. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/test_gpu.py +55 -0
  15. pystatistics-2.2.0/tests/mvnmle/test_mcar.py +128 -0
  16. pystatistics-2.2.0/tests/mvnmle/test_mom_mcar.py +169 -0
  17. pystatistics-2.2.0/tests/mvnmle/test_monotone.py +241 -0
  18. pystatistics-2.2.0/tests/mvnmle/test_no_silent_fallback.py +96 -0
  19. pystatistics-2.2.0/tests/mvnmle/test_squarem.py +120 -0
  20. pystatistics-2.0.1/pystatistics/mvnmle/backends/em.py +0 -330
  21. pystatistics-2.0.1/pystatistics/mvnmle/mcar_test.py +0 -228
  22. pystatistics-2.0.1/pystatistics/mvnmle/solvers.py +0 -195
  23. pystatistics-2.0.1/tests/mvnmle/test_no_silent_fallback.py +0 -55
  24. {pystatistics-2.0.1 → pystatistics-2.2.0}/.github/workflows/publish.yml +0 -0
  25. {pystatistics-2.0.1 → pystatistics-2.2.0}/.github/workflows/trigger-docs-rebuild.yml +0 -0
  26. {pystatistics-2.0.1 → pystatistics-2.2.0}/.gitignore +0 -0
  27. {pystatistics-2.0.1 → pystatistics-2.2.0}/.release/CHECKLIST.md +0 -0
  28. {pystatistics-2.0.1 → pystatistics-2.2.0}/.release/UNRELEASED.md +0 -0
  29. {pystatistics-2.0.1 → pystatistics-2.2.0}/.release/release.py +0 -0
  30. {pystatistics-2.0.1 → pystatistics-2.2.0}/CLAUDE.md +0 -0
  31. {pystatistics-2.0.1 → pystatistics-2.2.0}/LICENSE +0 -0
  32. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/DESIGN.md +0 -0
  33. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/Forge.md +0 -0
  34. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/GPU_BACKEND_NOTES.md +0 -0
  35. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/Makefile +0 -0
  36. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/PYSTATSBIO_CONTEXT.md +0 -0
  37. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/ROADMAP.md +0 -0
  38. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/_static/custom.css +0 -0
  39. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/anova.rst +0 -0
  40. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/conf.py +0 -0
  41. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/core.rst +0 -0
  42. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/descriptive.rst +0 -0
  43. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/hypothesis.rst +0 -0
  44. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/index.rst +0 -0
  45. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/mixed.rst +0 -0
  46. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/montecarlo.rst +0 -0
  47. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/mvnmle.rst +0 -0
  48. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/regression.rst +0 -0
  49. {pystatistics-2.0.1 → pystatistics-2.2.0}/docs/survival.rst +0 -0
  50. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/GPU_BACKEND_CONVENTION.md +0 -0
  51. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/__init__.py +0 -0
  52. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_common.py +0 -0
  53. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_contrasts.py +0 -0
  54. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_levene.py +0 -0
  55. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_posthoc.py +0 -0
  56. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_repeated.py +0 -0
  57. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/_ss.py +0 -0
  58. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/design.py +0 -0
  59. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/solution.py +0 -0
  60. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/anova/solvers.py +0 -0
  61. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/__init__.py +0 -0
  62. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/capabilities.py +0 -0
  63. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/__init__.py +0 -0
  64. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/device.py +0 -0
  65. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/__init__.py +0 -0
  66. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/batched.py +0 -0
  67. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/cholesky.py +0 -0
  68. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/determinant.py +0 -0
  69. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/qr.py +0 -0
  70. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/solve.py +0 -0
  71. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/linalg/svd.py +0 -0
  72. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/optimization/__init__.py +0 -0
  73. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/optimization/convergence.py +0 -0
  74. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/precision.py +0 -0
  75. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/timing.py +0 -0
  76. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/compute/tolerances.py +0 -0
  77. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/datasource.py +0 -0
  78. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/exceptions.py +0 -0
  79. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/protocols.py +0 -0
  80. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/result.py +0 -0
  81. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/core/validation.py +0 -0
  82. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/__init__.py +0 -0
  83. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/_missing.py +0 -0
  84. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/_quantile_types.py +0 -0
  85. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/backends/__init__.py +0 -0
  86. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/backends/cpu.py +0 -0
  87. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/backends/gpu.py +0 -0
  88. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/design.py +0 -0
  89. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/solution.py +0 -0
  90. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/descriptive/solvers.py +0 -0
  91. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/__init__.py +0 -0
  92. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_basis.py +0 -0
  93. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_common.py +0 -0
  94. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_fit.py +0 -0
  95. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_gam.py +0 -0
  96. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_gcv.py +0 -0
  97. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/_smooth.py +0 -0
  98. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/backends/__init__.py +0 -0
  99. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/backends/_gpu_family.py +0 -0
  100. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/backends/gpu_pirls.py +0 -0
  101. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/gam/solution.py +0 -0
  102. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/__init__.py +0 -0
  103. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/_common.py +0 -0
  104. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/_design_factories.py +0 -0
  105. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/_p_adjust.py +0 -0
  106. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/__init__.py +0 -0
  107. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_chisq_test.py +0 -0
  108. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_fisher_test.py +0 -0
  109. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_ks_test.py +0 -0
  110. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_prop_test.py +0 -0
  111. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_t_test.py +0 -0
  112. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_var_test.py +0 -0
  113. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/_wilcox_test.py +0 -0
  114. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/cpu.py +0 -0
  115. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/backends/gpu.py +0 -0
  116. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/design.py +0 -0
  117. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/solution.py +0 -0
  118. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/hypothesis/solvers.py +0 -0
  119. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/__init__.py +0 -0
  120. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_common.py +0 -0
  121. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_deviance.py +0 -0
  122. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_pirls.py +0 -0
  123. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_pls.py +0 -0
  124. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_random_effects.py +0 -0
  125. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/_satterthwaite.py +0 -0
  126. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/design.py +0 -0
  127. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/solution.py +0 -0
  128. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mixed/solvers.py +0 -0
  129. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/__init__.py +0 -0
  130. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/_ci.py +0 -0
  131. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/_common.py +0 -0
  132. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/_influence.py +0 -0
  133. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/backends/__init__.py +0 -0
  134. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/backends/cpu.py +0 -0
  135. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/backends/gpu.py +0 -0
  136. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/design.py +0 -0
  137. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/solution.py +0 -0
  138. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/montecarlo/solvers.py +0 -0
  139. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/__init__.py +0 -0
  140. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/_common.py +0 -0
  141. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/_likelihood.py +0 -0
  142. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/_solver.py +0 -0
  143. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/backends/__init__.py +0 -0
  144. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/backends/gpu_likelihood.py +0 -0
  145. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multinomial/solution.py +0 -0
  146. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/__init__.py +0 -0
  147. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/_common.py +0 -0
  148. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/_factor.py +0 -0
  149. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/_pca.py +0 -0
  150. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/_rotation.py +0 -0
  151. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/backends/__init__.py +0 -0
  152. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/multivariate/backends/gpu_pca.py +0 -0
  153. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/__init__.py +0 -0
  154. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/base.py +0 -0
  155. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/cpu.py +0 -0
  156. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/gpu_fp32.py +0 -0
  157. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/gpu_fp64.py +0 -0
  158. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_objectives/parameterizations.py +0 -0
  159. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/_utils.py +0 -0
  160. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/backends/__init__.py +0 -0
  161. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/backends/cpu.py +0 -0
  162. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/backends/gpu.py +0 -0
  163. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/datasets.py +0 -0
  164. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/design.py +0 -0
  165. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/patterns.py +0 -0
  166. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/mvnmle/solution.py +0 -0
  167. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/__init__.py +0 -0
  168. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/_common.py +0 -0
  169. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/_likelihood.py +0 -0
  170. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/_solver.py +0 -0
  171. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/backends/__init__.py +0 -0
  172. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/backends/gpu_likelihood.py +0 -0
  173. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/ordinal/solution.py +0 -0
  174. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/py.typed +0 -0
  175. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/__init__.py +0 -0
  176. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/_formatting.py +0 -0
  177. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/_glm.py +0 -0
  178. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/_linear.py +0 -0
  179. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/_nb_theta.py +0 -0
  180. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/backends/__init__.py +0 -0
  181. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/backends/cpu.py +0 -0
  182. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/backends/cpu_glm.py +0 -0
  183. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/backends/gpu.py +0 -0
  184. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/backends/gpu_glm.py +0 -0
  185. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/design.py +0 -0
  186. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/families.py +0 -0
  187. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/solution.py +0 -0
  188. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/regression/solvers.py +0 -0
  189. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/__init__.py +0 -0
  190. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/_common.py +0 -0
  191. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/_cox.py +0 -0
  192. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/_discrete.py +0 -0
  193. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/_km.py +0 -0
  194. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/_logrank.py +0 -0
  195. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/backends/__init__.py +0 -0
  196. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/backends/cpu.py +0 -0
  197. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/backends/gpu.py +0 -0
  198. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/design.py +0 -0
  199. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/solution.py +0 -0
  200. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/survival/solvers.py +0 -0
  201. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/__init__.py +0 -0
  202. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_acf.py +0 -0
  203. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_batch.py +0 -0
  204. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_factored.py +0 -0
  205. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_fit.py +0 -0
  206. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_forecast.py +0 -0
  207. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_kalman.py +0 -0
  208. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_likelihood.py +0 -0
  209. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_arima_order.py +0 -0
  210. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_common.py +0 -0
  211. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_decomposition.py +0 -0
  212. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_differencing.py +0 -0
  213. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_ets_fit.py +0 -0
  214. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_ets_forecast.py +0 -0
  215. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_ets_models.py +0 -0
  216. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_stationarity.py +0 -0
  217. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/_whittle.py +0 -0
  218. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/backends/__init__.py +0 -0
  219. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/backends/whittle_batch_gpu.py +0 -0
  220. {pystatistics-2.0.1 → pystatistics-2.2.0}/pystatistics/timeseries/backends/whittle_gpu.py +0 -0
  221. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/__init__.py +0 -0
  222. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/conftest.py +0 -0
  223. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_contrasts.py +0 -0
  224. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_design.py +0 -0
  225. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_factorial.py +0 -0
  226. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_levene.py +0 -0
  227. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_oneway.py +0 -0
  228. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_posthoc.py +0 -0
  229. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_r_validation.py +0 -0
  230. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/anova/test_repeated_measures.py +0 -0
  231. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/benchmark_gpu.py +0 -0
  232. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/conftest.py +0 -0
  233. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/core/test_datasource.py +0 -0
  234. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/core/test_exceptions.py +0 -0
  235. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/core/test_result.py +0 -0
  236. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/core/test_validation.py +0 -0
  237. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/__init__.py +0 -0
  238. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/conftest.py +0 -0
  239. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_cor.py +0 -0
  240. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_cov.py +0 -0
  241. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_describe.py +0 -0
  242. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_gpu.py +0 -0
  243. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_missing.py +0 -0
  244. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_moments.py +0 -0
  245. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_quantile.py +0 -0
  246. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/descriptive/test_r_validation.py +0 -0
  247. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_ancova_meta.json +0 -0
  248. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_ancova_r_results.json +0 -0
  249. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_bonferroni_meta.json +0 -0
  250. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_bonferroni_r_results.json +0 -0
  251. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_eta_meta.json +0 -0
  252. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_eta_r_results.json +0 -0
  253. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_levene_meta.json +0 -0
  254. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_levene_r_results.json +0 -0
  255. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_oneway_balanced_meta.json +0 -0
  256. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_oneway_balanced_r_results.json +0 -0
  257. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_oneway_unbalanced_meta.json +0 -0
  258. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_oneway_unbalanced_r_results.json +0 -0
  259. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_rm_mixed_meta.json +0 -0
  260. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_rm_mixed_r_results.json +0 -0
  261. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_rm_within_meta.json +0 -0
  262. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_rm_within_r_results.json +0 -0
  263. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_tukey_meta.json +0 -0
  264. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_tukey_r_results.json +0 -0
  265. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_twoway_balanced_meta.json +0 -0
  266. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_twoway_balanced_r_results.json +0 -0
  267. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_twoway_unbalanced_meta.json +0 -0
  268. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/anova_twoway_unbalanced_r_results.json +0 -0
  269. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/basic_100x3.csv +0 -0
  270. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/basic_100x3_meta.json +0 -0
  271. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/basic_100x3_r_results.json +0 -0
  272. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/collinear_almost.csv +0 -0
  273. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/collinear_almost_meta.json +0 -0
  274. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/collinear_almost_r_results.json +0 -0
  275. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_basic_100x5.csv +0 -0
  276. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_basic_100x5_meta.json +0 -0
  277. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_basic_100x5_r_results.json +0 -0
  278. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_constant_column.csv +0 -0
  279. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_constant_column_meta.json +0 -0
  280. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_constant_column_r_results.json +0 -0
  281. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_extreme_values.csv +0 -0
  282. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_extreme_values_meta.json +0 -0
  283. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_extreme_values_r_results.json +0 -0
  284. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_large_1000x10.csv +0 -0
  285. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_large_1000x10_meta.json +0 -0
  286. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_large_1000x10_r_results.json +0 -0
  287. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_columnwise.csv +0 -0
  288. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_columnwise_meta.json +0 -0
  289. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_columnwise_r_results.json +0 -0
  290. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_scattered.csv +0 -0
  291. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_scattered_meta.json +0 -0
  292. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_nan_scattered_r_results.json +0 -0
  293. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_negative_correlation.csv +0 -0
  294. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_negative_correlation_meta.json +0 -0
  295. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_negative_correlation_r_results.json +0 -0
  296. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_perfect_correlation.csv +0 -0
  297. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_perfect_correlation_meta.json +0 -0
  298. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_perfect_correlation_r_results.json +0 -0
  299. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_single_column.csv +0 -0
  300. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_single_column_meta.json +0 -0
  301. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_single_column_r_results.json +0 -0
  302. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_ties.csv +0 -0
  303. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_ties_meta.json +0 -0
  304. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/desc_ties_r_results.json +0 -0
  305. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/different_scales.csv +0 -0
  306. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/different_scales_meta.json +0 -0
  307. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/different_scales_r_results.json +0 -0
  308. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_anova_fixtures.py +0 -0
  309. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_descriptive_fixtures.py +0 -0
  310. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_fixtures.py +0 -0
  311. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_glm_fixtures.py +0 -0
  312. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_hypothesis_fixtures.py +0 -0
  313. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_mixed_fixtures.py +0 -0
  314. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_montecarlo_fixtures.py +0 -0
  315. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/generate_survival_fixtures.py +0 -0
  316. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_balanced.csv +0 -0
  317. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_balanced_meta.json +0 -0
  318. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_balanced_r_results.json +0 -0
  319. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_basic.csv +0 -0
  320. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_basic_meta.json +0 -0
  321. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_basic_r_results.json +0 -0
  322. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_large.csv +0 -0
  323. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_large_meta.json +0 -0
  324. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_large_r_results.json +0 -0
  325. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_separated.csv +0 -0
  326. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_separated_meta.json +0 -0
  327. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_binomial_separated_r_results.json +0 -0
  328. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_basic.csv +0 -0
  329. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_basic_meta.json +0 -0
  330. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_basic_r_results.json +0 -0
  331. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_large.csv +0 -0
  332. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_large_meta.json +0 -0
  333. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_gaussian_large_r_results.json +0 -0
  334. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_basic.csv +0 -0
  335. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_basic_meta.json +0 -0
  336. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_basic_r_results.json +0 -0
  337. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_large_counts.csv +0 -0
  338. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_large_counts_meta.json +0 -0
  339. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_large_counts_r_results.json +0 -0
  340. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_zeros.csv +0 -0
  341. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_zeros_meta.json +0 -0
  342. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/glm_poisson_zeros_r_results.json +0 -0
  343. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/high_noise.csv +0 -0
  344. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/high_noise_meta.json +0 -0
  345. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/high_noise_r_results.json +0 -0
  346. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_2x2_yates_meta.json +0 -0
  347. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_2x2_yates_r_results.json +0 -0
  348. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_3x3_meta.json +0 -0
  349. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_3x3_r_results.json +0 -0
  350. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_gof_meta.json +0 -0
  351. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_gof_r_results.json +0 -0
  352. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_gof_unequal_meta.json +0 -0
  353. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_chisq_gof_unequal_r_results.json +0 -0
  354. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_2x2_less_meta.json +0 -0
  355. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_2x2_less_r_results.json +0 -0
  356. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_2x2_meta.json +0 -0
  357. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_2x2_r_results.json +0 -0
  358. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_3x3_meta.json +0 -0
  359. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_fisher_3x3_r_results.json +0 -0
  360. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_ks_onesample_norm_meta.json +0 -0
  361. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_ks_onesample_norm_r_results.json +0 -0
  362. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_ks_twosample_meta.json +0 -0
  363. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_ks_twosample_r_results.json +0 -0
  364. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_prop_onesample_meta.json +0 -0
  365. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_prop_onesample_r_results.json +0 -0
  366. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_prop_twosample_meta.json +0 -0
  367. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_prop_twosample_r_results.json +0 -0
  368. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_onesample_meta.json +0 -0
  369. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_onesample_r_results.json +0 -0
  370. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_paired_meta.json +0 -0
  371. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_paired_r_results.json +0 -0
  372. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_pooled_meta.json +0 -0
  373. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_pooled_r_results.json +0 -0
  374. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_welch_meta.json +0 -0
  375. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_t_welch_r_results.json +0 -0
  376. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_var_basic_meta.json +0 -0
  377. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_var_basic_r_results.json +0 -0
  378. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_wilcox_ranksum_meta.json +0 -0
  379. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_wilcox_ranksum_r_results.json +0 -0
  380. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_wilcox_signed_meta.json +0 -0
  381. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/htest_wilcox_signed_r_results.json +0 -0
  382. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/ill_conditioned.csv +0 -0
  383. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/ill_conditioned_meta.json +0 -0
  384. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/ill_conditioned_r_results.json +0 -0
  385. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/large_coeffs.csv +0 -0
  386. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/large_coeffs_meta.json +0 -0
  387. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/large_coeffs_r_results.json +0 -0
  388. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_90_meta.json +0 -0
  389. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_90_r_results.json +0 -0
  390. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_normal_meta.json +0 -0
  391. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_normal_r_results.json +0 -0
  392. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_skewed_meta.json +0 -0
  393. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_ci_skewed_r_results.json +0 -0
  394. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_mean_balanced_meta.json +0 -0
  395. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_mean_balanced_r_results.json +0 -0
  396. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_mean_ordinary_meta.json +0 -0
  397. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_mean_ordinary_r_results.json +0 -0
  398. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_median_meta.json +0 -0
  399. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_median_r_results.json +0 -0
  400. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_variance_meta.json +0 -0
  401. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_boot_variance_r_results.json +0 -0
  402. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_greater_meta.json +0 -0
  403. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_greater_r_results.json +0 -0
  404. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_not_significant_meta.json +0 -0
  405. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_not_significant_r_results.json +0 -0
  406. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_significant_meta.json +0 -0
  407. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mc_perm_significant_r_results.json +0 -0
  408. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/glmm_binomial.csv +0 -0
  409. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/glmm_poisson.csv +0 -0
  410. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/lmm_crossed.csv +0 -0
  411. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/lmm_intercept.csv +0 -0
  412. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/lmm_ml.csv +0 -0
  413. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/lmm_no_effect.csv +0 -0
  414. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/lmm_slope.csv +0 -0
  415. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/mixed_meta.json +0 -0
  416. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/mixed/mixed_r_results.json +0 -0
  417. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/near_square.csv +0 -0
  418. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/near_square_meta.json +0 -0
  419. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/near_square_r_results.json +0 -0
  420. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/no_intercept.csv +0 -0
  421. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/no_intercept_meta.json +0 -0
  422. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/no_intercept_r_results.json +0 -0
  423. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_anova_validation.R +0 -0
  424. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_descriptive_validation.R +0 -0
  425. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_glm_validation.R +0 -0
  426. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_hypothesis_validation.R +0 -0
  427. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_mixed_validation.R +0 -0
  428. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_montecarlo_validation.R +0 -0
  429. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_survival_validation.R +0 -0
  430. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_r_validation.R +0 -0
  431. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/run_validation.sh +0 -0
  432. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/small_noise.csv +0 -0
  433. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/small_noise_meta.json +0 -0
  434. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/small_noise_r_results.json +0 -0
  435. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_breslow_meta.json +0 -0
  436. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_breslow_r_results.json +0 -0
  437. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_single_meta.json +0 -0
  438. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_single_r_results.json +0 -0
  439. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_ties_meta.json +0 -0
  440. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_ties_r_results.json +0 -0
  441. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_two_cov_meta.json +0 -0
  442. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_cox_two_cov_r_results.json +0 -0
  443. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_basic_meta.json +0 -0
  444. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_basic_r_results.json +0 -0
  445. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_heavy_cens_meta.json +0 -0
  446. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_heavy_cens_r_results.json +0 -0
  447. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_loglog_ci_meta.json +0 -0
  448. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_loglog_ci_r_results.json +0 -0
  449. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_no_cens_meta.json +0 -0
  450. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_no_cens_r_results.json +0 -0
  451. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_plain_ci_meta.json +0 -0
  452. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_plain_ci_r_results.json +0 -0
  453. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_ties_meta.json +0 -0
  454. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_km_ties_r_results.json +0 -0
  455. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_peto_meta.json +0 -0
  456. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_peto_r_results.json +0 -0
  457. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_three_group_meta.json +0 -0
  458. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_three_group_r_results.json +0 -0
  459. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_two_group_meta.json +0 -0
  460. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/surv_lr_two_group_r_results.json +0 -0
  461. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/tall_skinny.csv +0 -0
  462. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/tall_skinny_meta.json +0 -0
  463. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/tall_skinny_r_results.json +0 -0
  464. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/fixtures/validate_against_r.py +0 -0
  465. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/gam/__init__.py +0 -0
  466. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/gam/test_gam.py +0 -0
  467. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/__init__.py +0 -0
  468. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/conftest.py +0 -0
  469. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_chisq_test.py +0 -0
  470. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_design_split.py +0 -0
  471. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_fisher_test.py +0 -0
  472. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_gpu.py +0 -0
  473. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_ks_test.py +0 -0
  474. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_p_adjust.py +0 -0
  475. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_prop_test.py +0 -0
  476. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_r_validation.py +0 -0
  477. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_t_test.py +0 -0
  478. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_var_test.py +0 -0
  479. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/hypothesis/test_wilcox_test.py +0 -0
  480. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/__init__.py +0 -0
  481. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/conftest.py +0 -0
  482. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_glmm.py +0 -0
  483. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_lmm_crossed.py +0 -0
  484. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_lmm_intercept.py +0 -0
  485. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_lmm_nested.py +0 -0
  486. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_lmm_slope.py +0 -0
  487. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_pls.py +0 -0
  488. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_r_validation.py +0 -0
  489. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_random_effects.py +0 -0
  490. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mixed/test_satterthwaite.py +0 -0
  491. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/__init__.py +0 -0
  492. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/conftest.py +0 -0
  493. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_batched_solver.py +0 -0
  494. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_boot_ci.py +0 -0
  495. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_bootstrap.py +0 -0
  496. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_gpu.py +0 -0
  497. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_influence.py +0 -0
  498. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_permutation.py +0 -0
  499. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/montecarlo/test_r_validation.py +0 -0
  500. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/multinomial/__init__.py +0 -0
  501. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/multinomial/test_multinom.py +0 -0
  502. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/multivariate/__init__.py +0 -0
  503. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/multivariate/test_multivariate.py +0 -0
  504. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/apple_em_reference.json +0 -0
  505. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/apple_reference.json +0 -0
  506. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/generate_em_fixtures.R +0 -0
  507. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_apple.json +0 -0
  508. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_complete.json +0 -0
  509. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_extreme.json +0 -0
  510. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_missvals.json +0 -0
  511. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_simple_mcar.json +0 -0
  512. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/little_mcar_summary.json +0 -0
  513. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/missvals_em_reference.json +0 -0
  514. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/missvals_reference.json +0 -0
  515. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/references/small_test_reference.json +0 -0
  516. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/test_em.py +0 -0
  517. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/mvnmle/test_mlest.py +0 -0
  518. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/ordinal/__init__.py +0 -0
  519. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/ordinal/test_ordinal.py +0 -0
  520. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/benchmark.py +0 -0
  521. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/benchmark.r +0 -0
  522. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/conftest.py +0 -0
  523. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_fit.py +0 -0
  524. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_gamma_nb.py +0 -0
  525. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_glm.py +0 -0
  526. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_glm_gpu.py +0 -0
  527. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_glm_r_validation.py +0 -0
  528. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_module_split.py +0 -0
  529. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_r_validation.py +0 -0
  530. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/regression/test_stress_gpu.py +0 -0
  531. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/__init__.py +0 -0
  532. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/conftest.py +0 -0
  533. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_coxph.py +0 -0
  534. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_discrete_time.py +0 -0
  535. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_gpu.py +0 -0
  536. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_kaplan_meier.py +0 -0
  537. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_logrank.py +0 -0
  538. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/survival/test_r_validation.py +0 -0
  539. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/test_code_quality.py +0 -0
  540. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/timeseries/__init__.py +0 -0
  541. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/timeseries/test_acf_stationarity.py +0 -0
  542. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/timeseries/test_arima.py +0 -0
  543. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/timeseries/test_decomposition.py +0 -0
  544. {pystatistics-2.0.1 → pystatistics-2.2.0}/tests/timeseries/test_ets.py +0 -0
@@ -1,5 +1,282 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.0
4
+
5
+ - Fixed a `torch._C._LinAlgError` crash in `chi_square_mcar_batched_torch`
6
+ (`pystatistics/mvnmle/backends/_em_batched.py`) on GPU FP32. The batched
7
+ MoM fast path selected `cholesky_solve` when the SVD-based condition
8
+ number was below threshold, but Cholesky requires positive-definiteness,
9
+ which is strictly stronger than good conditioning. On GPU FP32, real
10
+ tabular data (e.g. `lacuna_tabular_110` applied to UCI/OpenML datasets)
11
+ can produce `sigma_oo` with good cond number but tiny negative eigenvalues
12
+ from roundoff, making Cholesky fail. Fix: wrap the fast path in
13
+ `try/except torch._C._LinAlgError`; on failure, fall back to
14
+ `torch.linalg.pinv` for the batch (honouring the `regularize` flag —
15
+ `regularize=False` still raises). Surfaced by dogfooding via Project
16
+ Lacuna on 3,080 (dataset × generator) pairs across the 110-generator
17
+ tabular registry; previously `mom_mcar_test` crashed on the first batch
18
+ containing breast_cancer / wine / credit_card_default.
19
+
20
+ - Fixed an exception-type leak in `little_mcar_test`
21
+ (`pystatistics/mvnmle/mcar_test.py:~250`). The ML-estimation try/except
22
+ wrapped *every* exception — including `PyStatisticsError` subclasses
23
+ like `NumericalError` — as a bare `RuntimeError`, breaking the
24
+ documented `except PyStatisticsError:` pattern downstream and losing
25
+ the original exception chain. Fix: explicitly re-raise
26
+ `PyStatisticsError`, and use `raise ... from e` for anything else so
27
+ the chain is preserved. Surfaced by Project Lacuna's cache builder,
28
+ which catches `PyStatisticsError` to fall back to a sentinel entry
29
+ when Little's test is numerically unfit for a particular
30
+ (dataset, generator) pair; MLE failures were leaking past the catch
31
+ and killing the whole build.
32
+
33
+ - Added ridge-fallback to the batched Cholesky sites inside the EM
34
+ E-step / log-likelihood
35
+ (`pystatistics/mvnmle/backends/_em_batched.py`):
36
+ `e_step_full_batched_np` (line ~361), `_e_step_full_torch` (~680), and
37
+ `_loglik_full_batched_torch` (~797). These compute per-pattern
38
+ Cholesky of sigma_oo sub-blocks; real tabular data can produce
39
+ individual sub-blocks that are numerically indefinite even when the
40
+ global sigma is PD (integer-encoded categoricals with heavy
41
+ collinearity in the intersection of a given missingness pattern's
42
+ observed variables). Fix: wrap each site in `try/except LinAlgError`
43
+ with a `ridge·I` retry (ridge = 1e-10 at pattern scale; statistically
44
+ invisible). Also removed a dead Cholesky call in `e_step_batched_np`
45
+ whose result was never used — it was only a crash liability.
46
+ `np.linalg.solve` at that same site now has a pinv fallback for
47
+ singular sub-blocks.
48
+
49
+ - Added `regularize: bool = True` to `mlest`, `_solve_em`, and
50
+ `EMBackend.solve`, mirroring the existing convention on
51
+ `mom_mcar_test` / `little_mcar_test`. When True (new default),
52
+ `EMBackend._ensure_pd` applies a small diagonal ridge
53
+ (`max(0, 1e-10 - min_eig) + 1e-12`) to the M-step sigma whenever its
54
+ smallest eigenvalue falls below the PD threshold, rather than raising
55
+ `NumericalError` outright. The ridge is vanishingly small relative to
56
+ any real data scale — the typical case the old path rejected had
57
+ min_eig ≈ 1e-13 from pure FP64 roundoff — and a UserWarning makes the
58
+ event visible in logs. Call sites that need strict bit-for-bit
59
+ behaviour pass `regularize=False`. Motivated by Project Lacuna
60
+ dogfooding: applying real missingness generators to real UCI datasets
61
+ (credit_card_default × MNAR-NonLinSocial produced min_eig ≈ -0.66) was
62
+ hard-raising and killing the full cache build; the ridge fallback
63
+ keeps the test numerically well-defined with negligible statistical
64
+ impact, and the build proceeds.
65
+
66
+
67
+ ## 2.1.0
68
+
69
+ - **`mom_mcar_test`: new method-of-moments MCAR test**
70
+ (``pystatistics/mvnmle/mcar_test.py``). A separate function — not
71
+ a new mode on ``little_mcar_test`` — because the method-of-moments
72
+ variant **is not Little's test**. Little (1988) specifically calls
73
+ for MLE plug-in estimators; swapping in pairwise-deletion sample
74
+ moments gives a statistic of the same shape with different
75
+ asymptotic properties, and calling it Little's test would be a
76
+ polite but concrete lie. The separate function preserves the
77
+ ``little_mcar_test`` contract exactly (matches R ``mvnmle`` bit-
78
+ for-bit as before) while giving users a documented fast alternative.
79
+
80
+ End-to-end timings at 15 % MCAR:
81
+
82
+ | dataset | shape | little_mcar_test | mom_mcar_test |
83
+ |----------------|-----------|------------------|---------------|
84
+ | iris | 150 × 4 | 2.9 ms | 0.31 ms |
85
+ | wine | 178 × 13 | 60.9 ms | 2.17 ms |
86
+ | breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
87
+
88
+ For repeated-diagnostic workflows (e.g. an MCAR sweep over 3410
89
+ datasets), this is **1.6 minutes vs ~50 minutes** end-to-end. The
90
+ statistical trade-off is asymptotic efficiency: MoM is consistent
91
+ under the MCAR null but not asymptotically efficient, and the
92
+ finite-sample distribution deviates more from chi-square than
93
+ Little's does. The docstring spells out when to use which:
94
+ diagnostic screens → MoM; regulated submissions or anywhere the
95
+ exact asymptotic distribution matters → Little's.
96
+
97
+ Implementation details:
98
+ - ``_pairwise_deletion_moments``: O(n v^2) pairwise mean and
99
+ covariance via a single matmul. No per-column loop.
100
+ - ``chi_square_mcar_batched_np`` / ``_torch``: fully batched
101
+ chi-square assembly (batched SVD for conditioning,
102
+ well-conditioned patterns through batched solve, ill-conditioned
103
+ patterns through batched pinv as separate groups — no
104
+ per-pattern Python loop).
105
+ - ``backend`` parameter with same size-heuristic + visible-warning
106
+ discipline as the EM path. GPU is supported but does not
107
+ out-perform CPU on any tested shape — MoM's compute is small
108
+ enough that transfer + launch overhead loses to CPU numpy.
109
+ Auto-dispatch warns when this is the case.
110
+ - Honesty: ``MCARTestResult`` gained a ``method`` field so
111
+ downstream code knows which test produced a given result.
112
+ ``little_mcar_test`` reports ``"Little (MLE plug-in)"``;
113
+ ``mom_mcar_test`` reports ``"Method-of-moments
114
+ (pairwise-deletion plug-in)"``.
115
+
116
+ New tests (``tests/mvnmle/test_mom_mcar.py``, 10 tests):
117
+ name-honesty, MLE-vs-MoM agreement on MCAR data, correct rejection
118
+ on non-MCAR data, all-missing-row handling, speed guard of
119
+ ≥ 10× over MLE on breast_cancer.
120
+
121
+ - **Fully-batched device-resident EM on GPU** (``_em_batched.py``
122
+ / ``_run_em_loop_gpu``). Pre-2.1.0 the "GPU EM" path set up a
123
+ torch device in the constructor but none of the per-iteration
124
+ work actually ran on-device — the numpy E-step ran for every
125
+ backend, which is why pre-2.1.0 benchmarks showed identical CPU
126
+ and GPU timings. This release implements the real thing: one
127
+ batched Cholesky + one batched solve over patterns for the
128
+ regression betas, one batched gather + bmm over all N
129
+ observations for the filled data, two dense gemms for the
130
+ sufficient-statistic accumulators, all on-device. SQUAREM also
131
+ runs fully on-device.
132
+
133
+ EM-only timings (without the MCAR-assembly wrapper):
134
+
135
+ | dataset | shape | CPU EM | GPU EM | speedup |
136
+ |----------------|-----------|----------|----------|---------|
137
+ | wine | 178 × 13 | 38 ms | 24 ms | 1.6× |
138
+ | breast_cancer | 569 × 30 | 2142 ms | 147 ms | 14.6× |
139
+
140
+ Small-data cases (apple, iris, missvals) lose on GPU because
141
+ transfer + launch overhead exceeds the per-iteration work.
142
+ Empirically calibrated heuristic: GPU is worth it when
143
+ ``n_obs * n_vars > 1500``.
144
+
145
+ - **Size-heuristic dispatch with Rule-1 visibility** for both EM and
146
+ MoM backends. When ``backend='auto'`` makes a non-obvious choice
147
+ (e.g. picking CPU despite GPU availability because the data are
148
+ too small), a ``UserWarning`` explains the decision and tells
149
+ users how to override. When ``backend='gpu'`` is explicitly
150
+ requested on small data, the request is honored (user knows best)
151
+ but a warning notes that CPU would likely be faster. No silent
152
+ fallbacks anywhere. New tests pin these behaviours.
153
+
154
+ - **Monotone-missingness closed-form MLE** (Anderson 1957; new
155
+ ``pystatistics.mvnmle._monotone``). When the missingness pattern
156
+ is monotone — when variables can be ordered such that each
157
+ observation's missing entries form a contiguous suffix — the MVN
158
+ MLE has a closed form via a chain of OLS regressions, with no
159
+ iteration. Common on longitudinal data with attrition, panel
160
+ surveys with dropout, and most sequentially-administered
161
+ instruments. New public helpers:
162
+
163
+ - ``pystatistics.mvnmle.is_monotone(data) -> bool``
164
+ - ``pystatistics.mvnmle.monotone_permutation(data) -> ndarray | None``
165
+ - ``pystatistics.mvnmle.mlest_monotone_closed_form(data) -> (mu, sigma, n)``
166
+ - ``mlest(data, algorithm='monotone')`` routes through the
167
+ closed-form; raises ``ValidationError`` if the data are not
168
+ monotone (Rule 1: no silent dispatch). Users who want
169
+ "use the closed form when applicable, fall back otherwise"
170
+ should call ``is_monotone`` first and branch explicitly.
171
+
172
+ The closed-form is the exact MLE (no tolerance-bounded
173
+ approximation), matches R ``mvnmle`` reference output on both
174
+ ``apple`` and ``missvals`` to machine precision, and is
175
+ dramatically faster than iterative algorithms at larger v
176
+ (a 1500 × 20 monotone dataset completes in ~2 ms vs EM's
177
+ ~40 ms). For non-monotone random MCAR data (the common case
178
+ in MCAR diagnostic use), detection is cheap (~O(v² n)) and
179
+ correctly returns False so iterative algorithms run.
180
+
181
+ New tests (``tests/mvnmle/test_monotone.py``, 12 tests):
182
+ detection true-positive / true-negative on several canonical
183
+ shapes; closed-form vs EM agreement; permutation invariance;
184
+ non-monotone data raises; performance guard at v=20.
185
+
186
+ - **EM MLE: substantial real-data speedup via batched per-pattern
187
+ linear algebra + SQUAREM acceleration** (Project Lacuna-driven).
188
+
189
+ End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
190
+
191
+ | dataset | shape | 2.0.1 | 2.1.0 | speedup |
192
+ |----------------|-----------|----------|----------|---------|
193
+ | apple | 18 × 2 | 1.9 ms | 2.0 ms | flat |
194
+ | missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
195
+ | iris | 150 × 4 | 2.8 ms | 2.8 ms | flat |
196
+ | wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
197
+ | breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
198
+
199
+ For workloads that run MCAR repeatedly over many datasets
200
+ (e.g. a 3410-entry MCAR sweep), this is roughly a 1-hour reduction
201
+ per full pass at Lacuna's current scale.
202
+
203
+ Three changes stack:
204
+
205
+ 1. **Batched per-pattern conditional parameters** (new
206
+ ``pystatistics.mvnmle.backends._em_batched``). The E-step used
207
+ to loop in Python over missingness patterns, issuing a scalar
208
+ Cholesky + triangular solve per pattern. It now stacks all P
209
+ pattern-sigma submatrices into a single
210
+ ``(P, v_max, v_max)`` tensor (identity-padded in the unused
211
+ slots so the Cholesky stays well-defined) and runs one batched
212
+ Cholesky + one batched solve for the whole iteration. The
213
+ accumulator loop over patterns remains in Python because
214
+ ``n_k`` varies and full observation-level padding hurt more
215
+ than it helped on the representative shapes we benchmarked.
216
+
217
+ 2. **SQUAREM acceleration** (Varadhan & Roland 2008; new
218
+ ``pystatistics.mvnmle.backends._squarem``). EM's linear
219
+ convergence is sped up by a Steffensen-style extrapolation of
220
+ three consecutive EM iterates, safeguarded by a monotonicity
221
+ check on the observed-data log-likelihood. Typical effect on
222
+ well-behaved EM problems: 2–4× reduction in underlying
223
+ EM-step-equivalents. Preserves the MLE — the convergence
224
+ point is unchanged, only the path is shorter. On by default
225
+ via a new ``accelerate=True`` kwarg on ``EMBackend.solve``;
226
+ pass ``accelerate=False`` for the plain-EM reference path.
227
+
228
+ 3. **Fully batched observed-data log-likelihood**
229
+ (``compute_loglik_batched_np``). The SQUAREM monotonicity
230
+ safeguard calls the log-likelihood often, so that path
231
+ needed to be cheap. The implementation now does one batched
232
+ Cholesky over all patterns for log-determinants and one
233
+ batched solve across all N observations for the quadratic-
234
+ form contribution — no per-pattern Python loop.
235
+
236
+ - **Benchmark harness** (``benchmarks/mvnmle_bench.py``). Runs the
237
+ five reference shapes (apple, missvals, iris, wine,
238
+ breast_cancer) across the (algorithm, backend) matrix and
239
+ prints wall-clock / iteration counts per case. Use
240
+ ``--quick`` to skip the BFGS cases that don't converge on
241
+ high-$v$ data; ``--tag`` labels a run for diff against prior
242
+ baselines.
243
+
244
+ - **Documented why direct-BFGS is not always the right default.**
245
+ Internal notes and the 2.0.0 / 2.0.1 release narrative already
246
+ covered why ``algorithm='em'`` became the ``little_mcar_test``
247
+ default; this release adds the story of why batching helps EM
248
+ significantly but does *not* rescue direct-BFGS on realistic
249
+ high-$v$ data (layer-3 Hessian conditioning is parameterization-
250
+ invariant; batching only addresses layer-1 launch overhead).
251
+ See ``GPU_BACKEND_CONVENTION.md`` Section 0 for the "when to
252
+ add a GPU backend and when not" rule that drove the 2.0.1
253
+ cleanup; this release extends that logic with
254
+ "accelerating the algorithm by reducing iteration count
255
+ (SQUAREM) is cheaper than accelerating each iteration."
256
+
257
+ - **Finding: the "GPU EM" backend was never actually running on
258
+ GPU.** The ``device='cuda'`` / ``'mps'`` constructor flag set
259
+ up ``self._torch`` but none of ``_e_step`` / ``_m_step`` /
260
+ ``_compute_loglik`` used it — the numpy path ran for every
261
+ backend. That's why pre-2.1.0 benchmarks showed identical
262
+ CPU and GPU EM timings. We attempted to implement a real
263
+ device-resident EM loop and found it was slower than CPU for
264
+ all the shapes we care about (per-pattern kernel-launch
265
+ overhead dominates the small per-pattern matrix work). The
266
+ honest answer for now is that GPU EM stays CPU-equivalent
267
+ by design; a future release may revisit with fully N-parallel
268
+ observation-level batching if a workload appears where the
269
+ GPU can actually win. This behaviour is unchanged from prior
270
+ releases — we're just documenting what was already true.
271
+
272
+ - **SQUAREM test coverage** (new ``tests/mvnmle/test_squarem.py``).
273
+ Four tests pinning the invariants: same MLE as plain EM on
274
+ apple; substantially fewer EM-equivalent steps on missvals;
275
+ monotonicity of log-likelihood preserved across iteration
276
+ caps; same MLE as plain EM on a realistic shape (sklearn
277
+ wine with 15 % MCAR).
278
+
279
+
3
280
  ## 2.0.1
4
281
 
5
282
  - **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.1
3
+ Version: 2.2.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,176 @@ GPU-accelerated statistical computing for Python.
51
51
 
52
52
  ## What's New
53
53
 
54
+ ### 2.2.0 — Real-data robustness from Project Lacuna dogfooding
55
+
56
+ Continuation of the 2.1.0 dogfooding track. Running `little_mcar_test`
57
+ and `mom_mcar_test` on 3,080 (dataset × generator) pairs drawn from 28
58
+ real UCI / OpenML / sklearn tabular datasets under
59
+ `lacuna_tabular_110` missingness generators surfaced four classes of
60
+ numerical failure that synthetic unit tests did not exhibit. All fixed
61
+ in this release; no API breaks.
62
+
63
+ **Batched MoM GPU Cholesky crash.** `chi_square_mcar_batched_torch`'s
64
+ fast path selected `cholesky_solve` whenever the SVD-based condition
65
+ number check passed, on the assumption that good conditioning implies
66
+ positive-definiteness. On GPU FP32, roundoff can produce covariances
67
+ that pass the cond-number check but have tiny negative eigenvalues —
68
+ Cholesky fails, the call raises `torch._C._LinAlgError`. The fast path
69
+ is now wrapped with `try/except` and falls back to pseudo-inverse when
70
+ the ``regularize`` flag allows. Surfaced on `credit_card_default` ×
71
+ `MNAR-NonLinSocial` during the Lacuna cache build.
72
+
73
+ **Exception-type preservation in `little_mcar_test`.** The
74
+ ML-estimation `try/except` at the top of `little_mcar_test` wrapped
75
+ *every* exception as a bare `RuntimeError` — including
76
+ `PyStatisticsError` subclasses. This broke the documented
77
+ `except PyStatisticsError:` catch pattern downstream: users falling
78
+ back to a sentinel on MLE failure saw their handler bypassed and the
79
+ full build crash. Fix: explicitly re-raise `PyStatisticsError`, and
80
+ use `raise ... from e` for anything else so the chain is preserved.
81
+
82
+ **`regularize=True` default on the EM path (opt-out).** `mlest`,
83
+ `_solve_em`, and `EMBackend.solve` gain `regularize: bool = True`,
84
+ mirroring the existing convention on `mom_mcar_test` and
85
+ `little_mcar_test`. When True, `EMBackend._ensure_pd` applies a small
86
+ diagonal ridge — `max(0, 1e-10 − min_eig) + 1e-12` — to the M-step
87
+ sigma whenever its smallest eigenvalue falls below the PD threshold,
88
+ rather than raising `NumericalError`. The ridge is well below any
89
+ statistical precision on real data — the typical case the old path
90
+ rejected had `min_eig ≈ 1e-13` from pure FP64 roundoff on a matrix
91
+ that's theoretically PSD. Dogfooding surfaced cases where `min_eig`
92
+ hit `−0.66` on realistic MNAR mechanisms; the ridge fallback keeps EM
93
+ progressing. Callers needing strict bit-for-bit behaviour pass
94
+ `regularize=False` to restore the old raise.
95
+
96
+ **Three additional Cholesky ridge-fallbacks** in `_em_batched.py`:
97
+ `e_step_full_batched_np`, `_e_step_full_torch`, and
98
+ `_loglik_full_batched_torch` all compute per-pattern Cholesky of
99
+ `sigma_oo` sub-blocks. Real tabular data can produce individual
100
+ sub-blocks that are numerically indefinite even when the global sigma
101
+ is PD (integer-encoded categoricals with heavy collinearity in the
102
+ intersection of a given missingness pattern's observed variables).
103
+ Each site now wraps Cholesky in `try/except LinAlgError` with a
104
+ `ridge·I` retry (ridge = 1e-10 at pattern scale; statistically
105
+ invisible). Also removed a dead Cholesky call in `e_step_batched_np`
106
+ whose factor was never used downstream — pure crash liability — and
107
+ added a `pinv` fallback to the `np.linalg.solve` at the same site for
108
+ singular sub-blocks.
109
+
110
+ **Impact.** The Project Lacuna cache build on 3,080 (dataset ×
111
+ generator) pairs went from crashing on the first batch containing
112
+ `breast_cancer` or `credit_card_default` (pre-2.2.0) to completing in
113
+ a single pass at 0.9% MoM sentinel rate and 16.4% MLE sentinel rate
114
+ (the MLE sentinels are legitimate EM non-convergence on 1000-pattern
115
+ datasets — not crashes). Synthetic unit tests: 125/125 mvnmle pass.
116
+
117
+ **No API breaks.** New defaults (`regularize=True`) are strictly more
118
+ permissive than the old raises — any caller that was crashing before
119
+ will now proceed with a small `UserWarning`. Callers needing strict
120
+ behaviour pass `regularize=False`.
121
+
122
+ ### 2.1.0 — Real-data EM speedup + monotone closed-form MLE
123
+
124
+ Dogfooding via Project Lacuna surfaced that ``little_mcar_test`` on
125
+ realistic tabular data (sklearn's iris / wine / breast_cancer with
126
+ random MCAR injection) was bottlenecked by EM: the E-step was a
127
+ Python loop over missingness patterns, and each SQUAREM-style
128
+ safeguard pass re-ran a per-pattern log-likelihood. This release
129
+ batches both and adds Varadhan & Roland's SQUAREM acceleration.
130
+
131
+ End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
132
+
133
+ | dataset | shape | 2.0.1 | 2.1.0 | speedup |
134
+ |----------------|-----------|----------|----------|---------|
135
+ | missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
136
+ | wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
137
+ | breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
138
+
139
+ For repeated-diagnostic workflows (e.g. an MCAR sweep over several
140
+ thousand datasets), this turns a 3-hour run into a 2-hour run.
141
+
142
+ Three stacked improvements, all preserving bit-equivalence on the R
143
+ mvnmle reference cases (apple, missvals):
144
+
145
+ - **Batched per-pattern conditional parameters.** The E-step's
146
+ per-pattern Cholesky + triangular solve now runs as a single
147
+ batched kernel pair across all missingness patterns. The
148
+ unused padding slots are identity-filled so the Cholesky stays
149
+ well-defined.
150
+ - **SQUAREM acceleration on top of EM.** Three EM steps + one
151
+ Steffensen-style extrapolation, safeguarded by a monotonicity
152
+ check on the observed-data log-likelihood. Typical effect:
153
+ 2–4× fewer EM-step equivalents to convergence. Convergence
154
+ point is the same MLE — only the path is shorter. On by
155
+ default; ``EMBackend.solve(..., accelerate=False)`` recovers
156
+ the plain-EM reference.
157
+ - **Fully batched log-likelihood.** The SQUAREM monotonicity
158
+ check calls ``loglik`` often, so it was batched too — one
159
+ Cholesky over all patterns, one solve across all N
160
+ observations, no per-pattern Python loop.
161
+
162
+ **`mom_mcar_test`: fast method-of-moments MCAR test.** A new *separate
163
+ function* (not a mode on ``little_mcar_test``, because the MoM variant
164
+ is not Little's test) that uses pairwise-deletion sample moments
165
+ instead of MLE plug-in. The test is consistent under MCAR but not
166
+ asymptotically efficient, trading a small amount of statistical
167
+ efficiency for dramatic speed. At 15 % MCAR on sklearn demos:
168
+
169
+ | dataset | shape | little_mcar_test | mom_mcar_test |
170
+ |----------------|-----------|------------------|---------------|
171
+ | iris | 150 × 4 | 2.9 ms | 0.31 ms |
172
+ | wine | 178 × 13 | 60.9 ms | 2.17 ms |
173
+ | breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
174
+
175
+ For a 3410-dataset MCAR sweep: **~50 minutes → ~1.6 minutes**. Use
176
+ ``little_mcar_test`` when you need Little 1988's asymptotic
177
+ distribution exactly (regulated submissions, citing R reference);
178
+ use ``mom_mcar_test`` for high-throughput diagnostic screens. The
179
+ ``MCARTestResult.method`` field records which test produced a given
180
+ result so downstream code can disambiguate without tracking the
181
+ calling function.
182
+
183
+ **Fully-batched device-resident EM on GPU.** Pre-2.1.0 the
184
+ ``device='cuda'`` EM path set up a torch device but never used it —
185
+ numpy ran for every backend. This release implements a real
186
+ device-resident loop with fully batched E-step / M-step / log-
187
+ likelihood, SQUAREM acceleration on top, all on device. On breast-
188
+ cancer-scale (569 × 30) EM drops from 2142 ms CPU to 147 ms GPU
189
+ (14.6×). Small data remains CPU-faster; an empirical size heuristic
190
+ (``n * v >= 1500``) with visible dispatch warnings keeps this
191
+ correct in user-facing behaviour.
192
+
193
+ **Monotone-missingness closed-form MLE** (Anderson 1957). Longitudinal
194
+ cohorts with attrition, panel surveys with dropout, and most
195
+ sequentially-administered instruments produce *monotone* missingness
196
+ — the variables can be ordered such that each observation's missing
197
+ entries form a contiguous suffix. When the pattern is monotone, the
198
+ MVN MLE has a closed form via a chain of OLS regressions, with no
199
+ iteration. New helpers: ``mvnmle.is_monotone(data)``,
200
+ ``mvnmle.monotone_permutation(data)``, and
201
+ ``mlest(data, algorithm='monotone')``. The closed-form matches R
202
+ ``mvnmle`` bit-for-bit on canonical datasets and is orders of
203
+ magnitude faster than EM on larger-v longitudinal data. Per Rule 1
204
+ the algorithm raises on non-monotone input rather than silently
205
+ falling back — call ``is_monotone`` first if you want conditional
206
+ dispatch.
207
+
208
+ Also in this release:
209
+
210
+ - **Benchmark harness** under ``benchmarks/mvnmle_bench.py`` for
211
+ tracking wall-clock and iteration counts across the reference
212
+ shapes; use the ``--tag`` flag to label a baseline for diff
213
+ against future changes.
214
+ - **Documented finding**: the ``device='cuda'`` EM path was never
215
+ actually running on the GPU prior to this release — it stored
216
+ a torch device but never used it. We tried to wire up a real
217
+ device-resident loop and found GPU is slower than CPU for all
218
+ shapes we tested (per-pattern launch overhead still dominates
219
+ the tiny per-pattern matrix work). GPU EM therefore remains
220
+ CPU-equivalent by design; a future release will revisit if a
221
+ workload appears where full observation-level batching makes
222
+ GPU actually win.
223
+
54
224
  ### 2.0.1 — GPU-backend exposure gaps and a convention rule
55
225
 
56
226
  Two public functions had GPU-capable inner calls but no `backend=`
@@ -4,6 +4,176 @@ GPU-accelerated statistical computing for Python.
4
4
 
5
5
  ## What's New
6
6
 
7
+ ### 2.2.0 — Real-data robustness from Project Lacuna dogfooding
8
+
9
+ Continuation of the 2.1.0 dogfooding track. Running `little_mcar_test`
10
+ and `mom_mcar_test` on 3,080 (dataset × generator) pairs drawn from 28
11
+ real UCI / OpenML / sklearn tabular datasets under
12
+ `lacuna_tabular_110` missingness generators surfaced four classes of
13
+ numerical failure that synthetic unit tests did not exhibit. All fixed
14
+ in this release; no API breaks.
15
+
16
+ **Batched MoM GPU Cholesky crash.** `chi_square_mcar_batched_torch`'s
17
+ fast path selected `cholesky_solve` whenever the SVD-based condition
18
+ number check passed, on the assumption that good conditioning implies
19
+ positive-definiteness. On GPU FP32, roundoff can produce covariances
20
+ that pass the cond-number check but have tiny negative eigenvalues —
21
+ Cholesky fails, the call raises `torch._C._LinAlgError`. The fast path
22
+ is now wrapped with `try/except` and falls back to pseudo-inverse when
23
+ the ``regularize`` flag allows. Surfaced on `credit_card_default` ×
24
+ `MNAR-NonLinSocial` during the Lacuna cache build.
25
+
26
+ **Exception-type preservation in `little_mcar_test`.** The
27
+ ML-estimation `try/except` at the top of `little_mcar_test` wrapped
28
+ *every* exception as a bare `RuntimeError` — including
29
+ `PyStatisticsError` subclasses. This broke the documented
30
+ `except PyStatisticsError:` catch pattern downstream: users falling
31
+ back to a sentinel on MLE failure saw their handler bypassed and the
32
+ full build crash. Fix: explicitly re-raise `PyStatisticsError`, and
33
+ use `raise ... from e` for anything else so the chain is preserved.
34
+
35
+ **`regularize=True` default on the EM path (opt-out).** `mlest`,
36
+ `_solve_em`, and `EMBackend.solve` gain `regularize: bool = True`,
37
+ mirroring the existing convention on `mom_mcar_test` and
38
+ `little_mcar_test`. When True, `EMBackend._ensure_pd` applies a small
39
+ diagonal ridge — `max(0, 1e-10 − min_eig) + 1e-12` — to the M-step
40
+ sigma whenever its smallest eigenvalue falls below the PD threshold,
41
+ rather than raising `NumericalError`. The ridge is well below any
42
+ statistical precision on real data — the typical case the old path
43
+ rejected had `min_eig ≈ 1e-13` from pure FP64 roundoff on a matrix
44
+ that's theoretically PSD. Dogfooding surfaced cases where `min_eig`
45
+ hit `−0.66` on realistic MNAR mechanisms; the ridge fallback keeps EM
46
+ progressing. Callers needing strict bit-for-bit behaviour pass
47
+ `regularize=False` to restore the old raise.
48
+
49
+ **Three additional Cholesky ridge-fallbacks** in `_em_batched.py`:
50
+ `e_step_full_batched_np`, `_e_step_full_torch`, and
51
+ `_loglik_full_batched_torch` all compute per-pattern Cholesky of
52
+ `sigma_oo` sub-blocks. Real tabular data can produce individual
53
+ sub-blocks that are numerically indefinite even when the global sigma
54
+ is PD (integer-encoded categoricals with heavy collinearity in the
55
+ intersection of a given missingness pattern's observed variables).
56
+ Each site now wraps Cholesky in `try/except LinAlgError` with a
57
+ `ridge·I` retry (ridge = 1e-10 at pattern scale; statistically
58
+ invisible). Also removed a dead Cholesky call in `e_step_batched_np`
59
+ whose factor was never used downstream — pure crash liability — and
60
+ added a `pinv` fallback to the `np.linalg.solve` at the same site for
61
+ singular sub-blocks.
62
+
63
+ **Impact.** The Project Lacuna cache build on 3,080 (dataset ×
64
+ generator) pairs went from crashing on the first batch containing
65
+ `breast_cancer` or `credit_card_default` (pre-2.2.0) to completing in
66
+ a single pass at 0.9% MoM sentinel rate and 16.4% MLE sentinel rate
67
+ (the MLE sentinels are legitimate EM non-convergence on 1000-pattern
68
+ datasets — not crashes). Synthetic unit tests: 125/125 mvnmle pass.
69
+
70
+ **No API breaks.** New defaults (`regularize=True`) are strictly more
71
+ permissive than the old raises — any caller that was crashing before
72
+ will now proceed with a small `UserWarning`. Callers needing strict
73
+ behaviour pass `regularize=False`.
74
+
75
+ ### 2.1.0 — Real-data EM speedup + monotone closed-form MLE
76
+
77
+ Dogfooding via Project Lacuna surfaced that ``little_mcar_test`` on
78
+ realistic tabular data (sklearn's iris / wine / breast_cancer with
79
+ random MCAR injection) was bottlenecked by EM: the E-step was a
80
+ Python loop over missingness patterns, and each SQUAREM-style
81
+ safeguard pass re-ran a per-pattern log-likelihood. This release
82
+ batches both and adds Varadhan & Roland's SQUAREM acceleration.
83
+
84
+ End-to-end ``little_mcar_test`` wall-clock at 15 % MCAR, seed 0:
85
+
86
+ | dataset | shape | 2.0.1 | 2.1.0 | speedup |
87
+ |----------------|-----------|----------|----------|---------|
88
+ | missvals | 13 × 5 | 19.9 ms | 9.5 ms | 2.1× |
89
+ | wine | 178 × 13 | 79.4 ms | 41.5 ms | 1.9× |
90
+ | breast_cancer | 569 × 30 | 3278 ms | 2089 ms | 1.6× |
91
+
92
+ For repeated-diagnostic workflows (e.g. an MCAR sweep over several
93
+ thousand datasets), this turns a 3-hour run into a 2-hour run.
94
+
95
+ Three stacked improvements, all preserving bit-equivalence on the R
96
+ mvnmle reference cases (apple, missvals):
97
+
98
+ - **Batched per-pattern conditional parameters.** The E-step's
99
+ per-pattern Cholesky + triangular solve now runs as a single
100
+ batched kernel pair across all missingness patterns. The
101
+ unused padding slots are identity-filled so the Cholesky stays
102
+ well-defined.
103
+ - **SQUAREM acceleration on top of EM.** Three EM steps + one
104
+ Steffensen-style extrapolation, safeguarded by a monotonicity
105
+ check on the observed-data log-likelihood. Typical effect:
106
+ 2–4× fewer EM-step equivalents to convergence. Convergence
107
+ point is the same MLE — only the path is shorter. On by
108
+ default; ``EMBackend.solve(..., accelerate=False)`` recovers
109
+ the plain-EM reference.
110
+ - **Fully batched log-likelihood.** The SQUAREM monotonicity
111
+ check calls ``loglik`` often, so it was batched too — one
112
+ Cholesky over all patterns, one solve across all N
113
+ observations, no per-pattern Python loop.
114
+
115
+ **`mom_mcar_test`: fast method-of-moments MCAR test.** A new *separate
116
+ function* (not a mode on ``little_mcar_test``, because the MoM variant
117
+ is not Little's test) that uses pairwise-deletion sample moments
118
+ instead of MLE plug-in. The test is consistent under MCAR but not
119
+ asymptotically efficient, trading a small amount of statistical
120
+ efficiency for dramatic speed. At 15 % MCAR on sklearn demos:
121
+
122
+ | dataset | shape | little_mcar_test | mom_mcar_test |
123
+ |----------------|-----------|------------------|---------------|
124
+ | iris | 150 × 4 | 2.9 ms | 0.31 ms |
125
+ | wine | 178 × 13 | 60.9 ms | 2.17 ms |
126
+ | breast_cancer | 569 × 30 | 1491 ms | 28.7 ms |
127
+
128
+ For a 3410-dataset MCAR sweep: **~50 minutes → ~1.6 minutes**. Use
129
+ ``little_mcar_test`` when you need Little 1988's asymptotic
130
+ distribution exactly (regulated submissions, citing R reference);
131
+ use ``mom_mcar_test`` for high-throughput diagnostic screens. The
132
+ ``MCARTestResult.method`` field records which test produced a given
133
+ result so downstream code can disambiguate without tracking the
134
+ calling function.
135
+
136
+ **Fully-batched device-resident EM on GPU.** Pre-2.1.0 the
137
+ ``device='cuda'`` EM path set up a torch device but never used it —
138
+ numpy ran for every backend. This release implements a real
139
+ device-resident loop with fully batched E-step / M-step / log-
140
+ likelihood, SQUAREM acceleration on top, all on device. On breast-
141
+ cancer-scale (569 × 30) EM drops from 2142 ms CPU to 147 ms GPU
142
+ (14.6×). Small data remains CPU-faster; an empirical size heuristic
143
+ (``n * v >= 1500``) with visible dispatch warnings keeps this
144
+ correct in user-facing behaviour.
145
+
146
+ **Monotone-missingness closed-form MLE** (Anderson 1957). Longitudinal
147
+ cohorts with attrition, panel surveys with dropout, and most
148
+ sequentially-administered instruments produce *monotone* missingness
149
+ — the variables can be ordered such that each observation's missing
150
+ entries form a contiguous suffix. When the pattern is monotone, the
151
+ MVN MLE has a closed form via a chain of OLS regressions, with no
152
+ iteration. New helpers: ``mvnmle.is_monotone(data)``,
153
+ ``mvnmle.monotone_permutation(data)``, and
154
+ ``mlest(data, algorithm='monotone')``. The closed-form matches R
155
+ ``mvnmle`` bit-for-bit on canonical datasets and is orders of
156
+ magnitude faster than EM on larger-v longitudinal data. Per Rule 1
157
+ the algorithm raises on non-monotone input rather than silently
158
+ falling back — call ``is_monotone`` first if you want conditional
159
+ dispatch.
160
+
161
+ Also in this release:
162
+
163
+ - **Benchmark harness** under ``benchmarks/mvnmle_bench.py`` for
164
+ tracking wall-clock and iteration counts across the reference
165
+ shapes; use the ``--tag`` flag to label a baseline for diff
166
+ against future changes.
167
+ - **Documented finding**: the ``device='cuda'`` EM path was never
168
+ actually running on the GPU prior to this release — it stored
169
+ a torch device but never used it. We tried to wire up a real
170
+ device-resident loop and found GPU is slower than CPU for all
171
+ shapes we tested (per-pattern launch overhead still dominates
172
+ the tiny per-pattern matrix work). GPU EM therefore remains
173
+ CPU-equivalent by design; a future release will revisit if a
174
+ workload appears where full observation-level batching makes
175
+ GPU actually win.
176
+
7
177
  ### 2.0.1 — GPU-backend exposure gaps and a convention rule
8
178
 
9
179
  Two public functions had GPU-capable inner calls but no `backend=`