bossanova 0.1.0.dev20__tar.gz → 0.1.0.dev21__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 (245) hide show
  1. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/.gitignore +1 -0
  2. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/PKG-INFO +1 -1
  3. bossanova-0.1.0.dev21/bossanova/distributions/__init__.py +63 -0
  4. bossanova-0.1.0.dev21/bossanova/distributions/continuous.py +45 -0
  5. bossanova-0.1.0.dev21/bossanova/distributions/discrete.py +217 -0
  6. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/distributions/varying.py +39 -58
  7. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/__init__.py +2 -0
  8. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/compare.py +16 -13
  9. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/cv.py +3 -3
  10. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/deviance.py +13 -4
  11. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/helpers.py +5 -10
  12. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/lrt.py +15 -12
  13. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/__init__.py +9 -5
  14. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/dataframes.py +0 -1
  15. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/resamples.py +2 -2
  16. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/specs.py +16 -16
  17. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/state.py +10 -0
  18. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/schemas.py +33 -19
  19. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/__init__.py +4 -4
  20. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/data.py +7 -7
  21. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/display.py +6 -3
  22. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/explore.py +2 -11
  23. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/state.py +16 -0
  24. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/validators.py +1 -4
  25. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/design/reference.py +23 -0
  26. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/design/z_matrix.py +1 -4
  27. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/__init__.py +6 -0
  28. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/convergence.py +3 -3
  29. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/dispatch.py +79 -3
  30. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/glm.py +2 -2
  31. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/glmer.py +2 -2
  32. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/predict.py +3 -3
  33. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/varying.py +76 -8
  34. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/bundle.py +87 -16
  35. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/contrast_specs.py +40 -1
  36. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/design.py +2 -7
  37. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/encoding.py +113 -73
  38. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_transforms.py +2 -2
  39. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/__init__.py +6 -6
  40. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/asymptotic.py +5 -4
  41. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/bootstrap.py +92 -45
  42. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/cv.py +213 -185
  43. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/dispatch.py +54 -2
  44. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/mee.py +2 -2
  45. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/permutation.py +29 -2
  46. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/profile.py +8 -9
  47. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/__init__.py +6 -6
  48. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/common.py +6 -6
  49. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/core.py +11 -11
  50. bossanova-0.1.0.dev21/bossanova/internal/infer/resample/results.py +53 -0
  51. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample_bundle.py +10 -10
  52. bossanova-0.1.0.dev21/bossanova/internal/infer/satterthwaite_emm.py +326 -0
  53. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/__init__.py +2 -2
  54. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/bracket_contrasts.py +77 -7
  55. bossanova-0.1.0.dev21/bossanova/internal/marginal/compute.py +1025 -0
  56. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/conditions.py +22 -1
  57. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/contrasts.py +131 -5
  58. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/emm.py +335 -40
  59. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore.py +8 -11
  60. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore_parser.py +37 -7
  61. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/grid.py +1 -1
  62. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/joint_tests.py +2 -2
  63. bossanova-0.1.0.dev21/bossanova/internal/marginal/resolve.py +365 -0
  64. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/slopes.py +50 -7
  65. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/transforms.py +37 -33
  66. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/__init__.py +6 -62
  67. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/dispatch.py +2 -1
  68. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/__init__.py +5 -3
  69. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/algebra.py +2 -0
  70. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/base.py +41 -0
  71. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/derived.py +4 -0
  72. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/factories.py +75 -12
  73. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/plotting.py +2 -0
  74. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/__init__.py +3 -3
  75. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/binomial.py +0 -4
  76. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/create.py +5 -5
  77. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/gamma.py +0 -4
  78. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/gaussian.py +0 -4
  79. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/links.py +0 -4
  80. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/poisson.py +0 -4
  81. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/schema.py +0 -4
  82. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/tdist.py +9 -7
  83. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/__init__.py +6 -6
  84. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/diagnostics.py +15 -9
  85. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/sandwich.py +3 -3
  86. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/satterthwaite.py +12 -16
  87. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/wald_variance.py +0 -5
  88. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/welch.py +13 -13
  89. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/__init__.py +4 -2
  90. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/qr.py +2 -6
  91. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/sparse.py +0 -10
  92. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/rng.py +3 -3
  93. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/glm.py +0 -37
  94. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/glmer.py +40 -15
  95. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_sparse.py +2 -11
  96. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_template.py +0 -4
  97. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/optimize.py +1 -1
  98. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/pirls_sparse.py +27 -15
  99. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/transforms.py +7 -6
  100. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/weights.py +0 -3
  101. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/generate.py +1 -14
  102. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/compare.py +27 -22
  103. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_data.py +53 -52
  104. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_protocols.py +6 -4
  105. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit.py +2 -1
  106. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit_builders.py +8 -7
  107. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/fit_layers.py +8 -7
  108. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/helpers.py +4 -2
  109. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/layout.py +0 -2
  110. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/mem.py +52 -28
  111. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/mem_forest.py +49 -20
  112. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/params.py +7 -6
  113. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/predict.py +9 -8
  114. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/ranef.py +16 -13
  115. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/resamples.py +5 -4
  116. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/resid.py +14 -13
  117. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/vif.py +2 -1
  118. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/model/core.py +128 -153
  119. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/model/summary.py +24 -23
  120. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/pyproject.toml +70 -5
  121. bossanova-0.1.0.dev20/bossanova/distributions/__init__.py +0 -53
  122. bossanova-0.1.0.dev20/bossanova/distributions/continuous.py +0 -273
  123. bossanova-0.1.0.dev20/bossanova/distributions/discrete.py +0 -274
  124. bossanova-0.1.0.dev20/bossanova/internal/infer/resample/results.py +0 -136
  125. bossanova-0.1.0.dev20/bossanova/internal/infer/satterthwaite_emm.py +0 -218
  126. bossanova-0.1.0.dev20/bossanova/internal/marginal/compute.py +0 -1219
  127. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/LICENSE +0 -0
  128. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/README.md +0 -0
  129. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/__init__.py +0 -0
  130. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/README.md +0 -0
  131. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/__init__.py +0 -0
  132. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/advertising.csv +0 -0
  133. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/cake.csv +0 -0
  134. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/chickweight.csv +0 -0
  135. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/credit.csv +0 -0
  136. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/gammas.csv +0 -0
  137. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/mtcars.csv +0 -0
  138. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/penguins.csv +0 -0
  139. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/poker.csv +0 -0
  140. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/sleep.csv +0 -0
  141. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/titanic.csv +0 -0
  142. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/titanic_test.csv +0 -0
  143. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/data/titanic_train.csv +0 -0
  144. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/expressions.py +0 -0
  145. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/__init__.py +0 -0
  146. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/f_test.py +4 -4
  147. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/ic.py +0 -0
  148. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/lrt_compare.py +1 -1
  149. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/compare/refit.py +0 -0
  150. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/__init__.py +0 -0
  151. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/builders/results.py +0 -0
  152. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/formula.py +0 -0
  153. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/containers/structs/specs.py +0 -0
  154. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/design/__init__.py +0 -0
  155. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/design/coding.py +0 -0
  156. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/design/names.py +0 -0
  157. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/diagnostics.py +0 -0
  158. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/lmer.py +0 -0
  159. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/fit/ols.py +0 -0
  160. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/__init__.py +0 -0
  161. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/contrast_registry.py +0 -0
  162. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate.py +0 -0
  163. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_contrast.py +0 -0
  164. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/evaluate_newdata.py +0 -0
  165. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/helpers.py +0 -0
  166. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parse.py +0 -0
  167. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/__init__.py +0 -0
  168. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/expr.py +0 -0
  169. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/parser.py +0 -0
  170. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/scanner.py +0 -0
  171. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/parser/token.py +0 -0
  172. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/formula/random_effects.py +0 -0
  173. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/formula_utils.py +0 -0
  174. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/params.py +0 -0
  175. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/prediction.py +0 -0
  176. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/glmer.py +0 -0
  177. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/lm_operators.py +0 -0
  178. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/lmer.py +0 -0
  179. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/resample/simulate.py +0 -0
  180. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/infer/simulation.py +0 -0
  181. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/explore_scanner.py +0 -0
  182. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/factors.py +0 -0
  183. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/inference.py +0 -0
  184. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/matrices.py +0 -0
  185. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/marginal/validation.py +0 -0
  186. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/__init__.py +0 -0
  187. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/jax.py +0 -0
  188. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/numpy.py +0 -0
  189. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/backend/protocol.py +0 -0
  190. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/batching.py +0 -0
  191. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/config.py +0 -0
  192. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/convergence.py +0 -0
  193. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/differentiation.py +0 -0
  194. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/core.py +0 -0
  195. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/distributions/probability.py +0 -0
  196. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/family/response.py +0 -0
  197. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/estimation.py +0 -0
  198. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/hypothesis.py +0 -0
  199. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/information_criteria.py +0 -0
  200. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/multiplicity.py +0 -0
  201. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/inference/profile.py +0 -0
  202. {bossanova-0.1.0.dev20/bossanova/internal/fit → bossanova-0.1.0.dev21/bossanova/internal/maths/linalg}/rank.py +0 -0
  203. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/schur.py +0 -0
  204. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/linalg/svd.py +0 -0
  205. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/predict.py +0 -0
  206. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/rounding.py +0 -0
  207. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/__init__.py +0 -0
  208. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/heuristics.py +0 -0
  209. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/initialization.py +0 -0
  210. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lambda_builder.py +0 -0
  211. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/lmer.py +0 -0
  212. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/solvers/quadrature.py +0 -0
  213. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/tolerances.py +0 -0
  214. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/maths/variance.py +0 -0
  215. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/__init__.py +0 -0
  216. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/latex.py +0 -0
  217. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/rendering/markdown.py +0 -0
  218. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/__init__.py +0 -0
  219. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/__init__.py +0 -0
  220. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/glm.py +0 -0
  221. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/glmer.py +0 -0
  222. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/lm.py +0 -0
  223. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/dgp/lmer.py +0 -0
  224. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/harness.py +0 -0
  225. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/metrics.py +0 -0
  226. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/model_sim.py +0 -0
  227. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/simulation/power.py +0 -0
  228. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/README.md +0 -0
  229. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/__init__.py +0 -0
  230. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/cognition.py +0 -0
  231. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core.py +0 -0
  232. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_sizing.py +0 -0
  233. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/core_viz.py +0 -0
  234. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/dag.py +0 -0
  235. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/design.py +0 -0
  236. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/lattice.py +0 -0
  237. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/profile.py +0 -0
  238. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/internal/viz/relationships.py +0 -0
  239. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/model/__init__.py +0 -0
  240. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/model/guards.py +0 -0
  241. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/model/result.py +0 -0
  242. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/bossanova/py.typed +0 -0
  243. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/tests/bossanova_benchmarks/bootstrap/data/README.md +0 -0
  244. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/tests/bossanova_benchmarks/insteval/data/README.md +0 -0
  245. {bossanova-0.1.0.dev20 → bossanova-0.1.0.dev21}/tests/bossanova_tests/hypothesis/README.md +0 -0
@@ -42,6 +42,7 @@ tests/parity/traces/
42
42
  # Coverage reports
43
43
  coverage_reports/
44
44
  .coverage
45
+ .coverage.*
45
46
  htmlcov/
46
47
  # pixi environments
47
48
  .pixi/*
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bossanova
3
- Version: 0.1.0.dev20
3
+ Version: 0.1.0.dev21
4
4
  Summary: Bridging statistical cultures with some jazz
5
5
  Author: Eshin Jolly
6
6
  License-Expression: MIT
@@ -0,0 +1,63 @@
1
+ """Distribution factories for simulation and probability queries.
2
+
3
+ Users get rich Distribution objects with plotting, algebra, and probability::
4
+
5
+ d = normal(0, 1)
6
+ d # In notebooks: renders PDF plot
7
+ d + normal(3, 2) # Convolution -> Normal(3, sqrt(5))
8
+ d > 1.96 # P(X > 1.96) = 0.025
9
+ d.pdf(0) # 0.3989
10
+ """
11
+
12
+ from bossanova.distributions.continuous import (
13
+ Distribution,
14
+ exponential,
15
+ gamma,
16
+ normal,
17
+ t,
18
+ t_dist,
19
+ uniform,
20
+ )
21
+ from bossanova.distributions.discrete import (
22
+ Categorical,
23
+ binomial,
24
+ categorical,
25
+ poisson,
26
+ )
27
+ from bossanova.distributions.varying import Varying, varying
28
+
29
+ # Rich internal types also available
30
+ from bossanova.internal.maths.distributions import (
31
+ ConvolvedDistribution,
32
+ f_dist,
33
+ FoldedDistribution,
34
+ Probability,
35
+ TransformedDistribution,
36
+ TruncatedDistribution,
37
+ beta,
38
+ chi2,
39
+ )
40
+
41
+ __all__ = [
42
+ "Categorical",
43
+ "ConvolvedDistribution",
44
+ "Distribution",
45
+ "FoldedDistribution",
46
+ "Probability",
47
+ "TransformedDistribution",
48
+ "TruncatedDistribution",
49
+ "Varying",
50
+ "beta",
51
+ "binomial",
52
+ "categorical",
53
+ "chi2",
54
+ "exponential",
55
+ "f_dist",
56
+ "gamma",
57
+ "normal",
58
+ "poisson",
59
+ "t",
60
+ "t_dist",
61
+ "uniform",
62
+ "varying",
63
+ ]
@@ -0,0 +1,45 @@
1
+ """Continuous distribution factories — thin facade over internal/maths/distributions/.
2
+
3
+ Re-exports internal Distribution factories with user-facing parameterization.
4
+ Users get rich Distribution objects with plotting, algebra, and probability queries.
5
+ """
6
+
7
+ from bossanova.internal.maths.distributions.base import Distribution
8
+ from bossanova.internal.maths.distributions.factories import (
9
+ exponential,
10
+ gamma as _internal_gamma,
11
+ normal,
12
+ t,
13
+ t_dist,
14
+ uniform,
15
+ )
16
+
17
+
18
+ def gamma(shape: float, scale: float = 1.0) -> Distribution:
19
+ """Create a gamma distribution (scale parameterization only).
20
+
21
+ Args:
22
+ shape: Shape parameter (must be positive).
23
+ scale: Scale parameter (must be positive). Default is 1.0.
24
+
25
+ Returns:
26
+ Distribution object.
27
+
28
+ Examples:
29
+ ```python
30
+ g = gamma(shape=2.0) # Gamma(2, 1), mean = 2
31
+ g = gamma(shape=2.0, scale=0.5) # Gamma(2, 0.5), mean = 1
32
+ ```
33
+ """
34
+ return _internal_gamma(shape=shape, scale=scale)
35
+
36
+
37
+ __all__ = [
38
+ "Distribution",
39
+ "exponential",
40
+ "gamma",
41
+ "normal",
42
+ "t",
43
+ "t_dist",
44
+ "uniform",
45
+ ]
@@ -0,0 +1,217 @@
1
+ """Discrete distribution factories — thin facade.
2
+
3
+ Categorical stays as a native attrs struct (no internal counterpart).
4
+ Poisson and Binomial delegate to internal Distribution objects.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import numpy as np
10
+ from attrs import frozen, field
11
+ from scipy import stats
12
+
13
+ from bossanova.internal.maths.distributions.base import Distribution
14
+
15
+
16
+ @frozen
17
+ class Categorical:
18
+ """Categorical distribution for simulation.
19
+
20
+ Generates categorical values from specified levels with optional probabilities.
21
+
22
+ Args:
23
+ levels: Category names. Either provide explicit levels or use k= for auto-naming.
24
+ p: Probabilities for each level. Must sum to 1. If None, uniform distribution.
25
+ k: Number of levels for auto-naming (level_1, level_2, ...).
26
+ Ignored if levels is provided.
27
+
28
+ Raises:
29
+ ValueError: If neither levels nor k is provided.
30
+ ValueError: If both levels and k are provided.
31
+ ValueError: If p is provided without levels.
32
+ ValueError: If the length of p does not match the length of levels.
33
+ ValueError: If p does not sum to 1.
34
+
35
+ Examples:
36
+ ```python
37
+ from bossanova.distributions import categorical
38
+
39
+ cat = categorical(levels=["A", "B", "C"])
40
+ rng = np.random.default_rng(42)
41
+ samples = cat.sample(5, rng) # shape (5,)
42
+
43
+ cat = categorical(levels=["A", "B"], p=[0.8, 0.2]) # mostly "A"
44
+
45
+ cat = categorical(k=4) # level_1, level_2, level_3, level_4
46
+ ```
47
+ """
48
+
49
+ levels: tuple[str, ...] | None = field(
50
+ default=None, converter=lambda x: tuple(x) if x is not None else None
51
+ )
52
+ p: tuple[float, ...] | None = field(
53
+ default=None, converter=lambda x: tuple(x) if x is not None else None
54
+ )
55
+ k: int | None = field(default=None)
56
+
57
+ def __attrs_post_init__(self) -> None:
58
+ """Validate that levels/k and p are consistent."""
59
+ # Must have either levels or k
60
+ if self.levels is None and self.k is None:
61
+ msg = "Must specify either levels or k"
62
+ raise ValueError(msg)
63
+ if self.levels is not None and self.k is not None:
64
+ msg = "Specify either levels or k, not both"
65
+ raise ValueError(msg)
66
+
67
+ # Validate p if provided
68
+ if self.p is not None:
69
+ if self.levels is None:
70
+ msg = "p requires levels to be specified"
71
+ raise ValueError(msg)
72
+ if len(self.p) != len(self.levels):
73
+ msg = f"p length ({len(self.p)}) must match levels length ({len(self.levels)})"
74
+ raise ValueError(msg)
75
+ if not np.isclose(sum(self.p), 1.0):
76
+ msg = f"p must sum to 1.0, got {sum(self.p)}"
77
+ raise ValueError(msg)
78
+
79
+ @property
80
+ def actual_levels(self) -> tuple[str, ...]:
81
+ """Return the actual level names (resolving k if needed)."""
82
+ if self.levels is not None:
83
+ return self.levels
84
+ return tuple(f"level_{i + 1}" for i in range(self.k)) # type: ignore[arg-type]
85
+
86
+ @property
87
+ def n_levels(self) -> int:
88
+ """Return the number of levels."""
89
+ return len(self.actual_levels)
90
+
91
+ def sample(self, n: int, rng: np.random.Generator) -> np.ndarray:
92
+ """Sample n categorical values.
93
+
94
+ Args:
95
+ n: Number of samples to draw.
96
+ rng: NumPy random number generator.
97
+
98
+ Returns:
99
+ Array of n categorical values as strings.
100
+ """
101
+ levels = self.actual_levels
102
+ probs = self.p if self.p is not None else None
103
+ indices = rng.choice(len(levels), size=n, p=probs)
104
+ return np.array([levels[i] for i in indices])
105
+
106
+
107
+ # =============================================================================
108
+ # Factory functions
109
+ # =============================================================================
110
+
111
+
112
+ def categorical(
113
+ levels: list[str] | None = None,
114
+ *,
115
+ p: list[float] | None = None,
116
+ k: int | None = None,
117
+ ) -> Categorical:
118
+ """Create a categorical distribution.
119
+
120
+ Args:
121
+ levels: Category names. Either provide explicit levels or use k= for
122
+ auto-naming (level_1, level_2, ...). Mutually exclusive with k.
123
+ p: Probabilities for each level. Must sum to 1. Length must match
124
+ levels. If None, a uniform distribution over levels is used.
125
+ Requires levels to be specified.
126
+ k: Number of levels for auto-naming (level_1, level_2, ...).
127
+ Mutually exclusive with levels.
128
+
129
+ Returns:
130
+ Categorical distribution object.
131
+
132
+ Raises:
133
+ ValueError: If neither levels nor k is provided.
134
+ ValueError: If both levels and k are provided.
135
+ ValueError: If p is provided without levels.
136
+ ValueError: If the length of p does not match the length of levels.
137
+ ValueError: If p does not sum to 1.
138
+
139
+ Examples:
140
+ ```python
141
+ categorical(["A", "B", "C"])
142
+ # Categorical(levels=('A', 'B', 'C'), p=None, k=None)
143
+
144
+ categorical(["A", "B"], p=[0.7, 0.3])
145
+ # Categorical(levels=('A', 'B'), p=(0.7, 0.3), k=None)
146
+
147
+ categorical(k=4) # level_1, level_2, level_3, level_4
148
+ # Categorical(levels=None, p=None, k=4)
149
+ ```
150
+ """
151
+ return Categorical(levels=levels, p=p, k=k)
152
+
153
+
154
+ def poisson(rate: float = 1.0) -> Distribution:
155
+ """Create a Poisson distribution.
156
+
157
+ Args:
158
+ rate: Rate parameter (lambda). Must be > 0. Default is 1.0.
159
+
160
+ Returns:
161
+ Distribution object.
162
+
163
+ Raises:
164
+ ValueError: If rate is not positive.
165
+
166
+ Examples:
167
+ ```python
168
+ dist = poisson() # Poisson(1)
169
+ dist = poisson(rate=5.0) # Poisson(5), mean = 5
170
+ ```
171
+ """
172
+ if rate <= 0:
173
+ raise ValueError("rate must be > 0")
174
+ return Distribution(
175
+ dist=stats.poisson(mu=rate),
176
+ name="Poisson",
177
+ params={"rate": rate},
178
+ )
179
+
180
+
181
+ def binomial(n: int = 1, p: float = 0.5) -> Distribution:
182
+ """Create a binomial distribution.
183
+
184
+ Args:
185
+ n: Number of trials. Must be >= 1. Default is 1 (Bernoulli).
186
+ p: Probability of success. Must be in [0, 1]. Default is 0.5.
187
+
188
+ Returns:
189
+ Distribution object.
190
+
191
+ Raises:
192
+ ValueError: If n is not an integer >= 1 or p is not in [0, 1].
193
+
194
+ Examples:
195
+ ```python
196
+ dist = binomial() # Bernoulli(0.5)
197
+ dist = binomial(n=10, p=0.3) # Binomial(10, 0.3), mean = 3
198
+ ```
199
+ """
200
+ if not isinstance(n, int) or n < 1:
201
+ raise ValueError("n must be an integer >= 1")
202
+ if not 0 <= p <= 1:
203
+ raise ValueError(f"p ({p}) must be in [0, 1]")
204
+ return Distribution(
205
+ dist=stats.binom(n=n, p=p),
206
+ name="Binomial",
207
+ params={"n": n, "p": p},
208
+ )
209
+
210
+
211
+ __all__ = [
212
+ "Categorical",
213
+ "Distribution",
214
+ "binomial",
215
+ "categorical",
216
+ "poisson",
217
+ ]
@@ -3,6 +3,8 @@
3
3
  from attrs import frozen, field, validators
4
4
  import numpy as np
5
5
 
6
+ __all__ = ["Varying", "varying"]
7
+
6
8
 
7
9
  @frozen
8
10
  class Varying:
@@ -15,27 +17,22 @@ class Varying:
15
17
  sd (float): Standard deviation for random intercept. Default 1.0.
16
18
  intercept_sd (float | None): Alias for sd (for clarity with random slopes).
17
19
  slope_sds (dict[str, float]): Dict mapping term names to their random slope SDs.
18
- corr (float | dict[tuple[str, str], float]): Correlation between intercept
19
- and slope(s). Single value or dict.
20
+ corr (float): Correlation between intercept and slope(s). Applied
21
+ uniformly across all term pairs. Default 0.0.
20
22
  n_per (int | None): For nested effects, number of sub-groups per parent group.
21
23
 
22
24
  Examples:
23
- Random intercept only: (1|subject)::
24
-
25
- >>> varying(n=50, sd=0.3)
26
- Varying(n=50, sd=0.3, ...)
27
-
28
- Random intercept + slope: (1 + time|subject)::
29
-
30
- >>> varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
31
- Varying(n=50, intercept_sd=0.3, slope_sds={'time': 0.1}, corr=0.2, ...)
25
+ ```python
26
+ # Random intercept only: (1|subject)
27
+ varying(n=50, sd=0.3)
32
28
 
33
- Nested effects: (1|school/class) - 3 classes per school::
29
+ # Random intercept + slope: (1 + time|subject)
30
+ varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
34
31
 
35
- >>> varying(n=10, sd=0.4) # schools
36
- Varying(n=10, sd=0.4, ...)
37
- >>> varying(n_per=3, sd=0.2) # classes nested in schools
38
- Varying(n_per=3, sd=0.2, ...)
32
+ # Nested effects: (1|school/class) - 3 classes per school
33
+ varying(n=10, sd=0.4) # schools
34
+ varying(n_per=3, sd=0.2) # classes nested in schools
35
+ ```
39
36
  """
40
37
 
41
38
  n: int | None = field(default=None)
@@ -44,7 +41,7 @@ class Varying:
44
41
  # For random slopes
45
42
  intercept_sd: float | None = field(default=None)
46
43
  slope_sds: dict[str, float] = field(factory=dict)
47
- corr: float | dict[tuple[str, str], float] = field(default=0.0)
44
+ corr: float = field(default=0.0)
48
45
 
49
46
  # For nested effects
50
47
  n_per: int | None = field(default=None)
@@ -72,15 +69,15 @@ class Varying:
72
69
  raise ValueError(msg)
73
70
 
74
71
  # Validate correlation
75
- if isinstance(self.corr, (int, float)):
76
- if not -1 <= self.corr <= 1:
77
- msg = f"corr must be in [-1, 1], got {self.corr}"
78
- raise ValueError(msg)
79
- elif isinstance(self.corr, dict):
80
- for key, val in self.corr.items():
81
- if not -1 <= val <= 1:
82
- msg = f"corr[{key}] must be in [-1, 1], got {val}"
83
- raise ValueError(msg)
72
+ if isinstance(self.corr, dict):
73
+ msg = (
74
+ "Dict-form corr is not yet supported. "
75
+ "Use a single scalar value for uniform correlation across all pairs."
76
+ )
77
+ raise NotImplementedError(msg)
78
+ if not -1 <= self.corr <= 1:
79
+ msg = f"corr must be in [-1, 1], got {self.corr}"
80
+ raise ValueError(msg)
84
81
 
85
82
  @property
86
83
  def effective_intercept_sd(self) -> float:
@@ -160,15 +157,8 @@ class Varying:
160
157
  self.slope_sds[t] for t in sorted(self.slope_sds)
161
158
  ]
162
159
 
163
- if isinstance(self.corr, (int, float)):
164
- # Single correlation for all pairs
165
- corr_matrix = np.full((n_terms, n_terms), self.corr)
166
- np.fill_diagonal(corr_matrix, 1.0)
167
- else:
168
- # Build from dict - default to 0 for unspecified pairs
169
- corr_matrix = np.eye(n_terms)
170
- # Note: dict-based correlation not fully implemented for simplicity
171
- # Users should use single corr value for now
160
+ corr_matrix = np.full((n_terms, n_terms), self.corr)
161
+ np.fill_diagonal(corr_matrix, 1.0)
172
162
 
173
163
  # Convert correlation to covariance: Cov = D @ Corr @ D
174
164
  D = np.diag(sds)
@@ -183,7 +173,7 @@ def varying(
183
173
  sd: float = 1.0,
184
174
  intercept_sd: float | None = None,
185
175
  slope_sds: dict[str, float] | None = None,
186
- corr: float | dict[tuple[str, str], float] = 0.0,
176
+ corr: float = 0.0,
187
177
  n_per: int | None = None,
188
178
  ) -> Varying:
189
179
  """Create a varying (random effect) distribution specification.
@@ -197,37 +187,28 @@ def varying(
197
187
  intercept_sd (float | None): Alias for sd (for clarity with random slopes).
198
188
  slope_sds (dict[str, float] | None): Dict mapping term names to their
199
189
  random slope SDs.
200
- corr (float | dict[tuple[str, str], float]): Correlation between intercept
201
- and slope(s). Single value or dict.
190
+ corr (float): Correlation between intercept and slope(s). Applied
191
+ uniformly across all term pairs. Default 0.0.
202
192
  n_per (int | None): For nested effects, number of sub-groups per parent group.
203
193
 
204
194
  Returns:
205
195
  Varying specification for use in model.simulate().
206
196
 
207
197
  Examples:
208
- Random intercept only: (1|subject)::
209
-
210
- >>> from bossanova.distributions import varying
211
- >>> v = varying(n=50, sd=0.3)
212
- >>> v.n
213
- 50
214
- >>> v.sd
215
- 0.3
216
-
217
- Random intercept + slope: (1 + time|subject)::
198
+ ```python
199
+ from bossanova.distributions import varying
218
200
 
219
- >>> v = varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
220
- >>> v.has_random_slopes
221
- True
222
- >>> v.n_terms
223
- 2
201
+ # Random intercept only: (1|subject)
202
+ v = varying(n=50, sd=0.3) # v.n == 50, v.sd == 0.3
224
203
 
225
- Nested effects: (1|school/class) - 3 classes per school::
204
+ # Random intercept + slope: (1 + time|subject)
205
+ v = varying(n=50, intercept_sd=0.3, slope_sds={"time": 0.1}, corr=0.2)
206
+ # v.has_random_slopes == True, v.n_terms == 2
226
207
 
227
- >>> schools = varying(n=10, sd=0.4) # schools
228
- >>> classes = varying(n_per=3, sd=0.2) # classes nested in schools
229
- >>> classes.is_nested
230
- True
208
+ # Nested effects: (1|school/class) - 3 classes per school
209
+ schools = varying(n=10, sd=0.4)
210
+ classes = varying(n_per=3, sd=0.2) # classes.is_nested == True
211
+ ```
231
212
  """
232
213
  return Varying(
233
214
  n=n,
@@ -1 +1,3 @@
1
1
  """Internal implementation modules — not part of the public API."""
2
+
3
+ __all__: list[str] = []
@@ -82,7 +82,7 @@ def compare(
82
82
  - "aic": AIC comparison with delta-AIC and Akaike weights
83
83
  - "bic": BIC comparison with delta-BIC and Schwarz weights
84
84
  sort: If True, sort models by complexity before comparing.
85
- This ensures proper nesting order.
85
+ This ensures proper nesting order. Default True.
86
86
  refit: If True and models are lmer/glmer with REML estimation,
87
87
  automatically refit with ML for valid LRT comparison.
88
88
  Original models are not mutated. Default False.
@@ -157,34 +157,38 @@ def compare(
157
157
 
158
158
  Examples:
159
159
  ```python
160
+ from bossanova import model, compare, load_dataset
161
+ mtcars = load_dataset("mtcars")
162
+ sleepstudy = load_dataset("sleepstudy")
163
+
160
164
  # Models are auto-fitted if needed (calls .fit() with defaults)
161
- compare(lm("mpg ~ 1", data=mtcars), lm("mpg ~ wt", data=mtcars))
165
+ compare(model("mpg ~ 1", mtcars), model("mpg ~ wt", mtcars))
162
166
 
163
167
  # Equivalent to explicit .fit() calls:
164
- compare(lm("mpg ~ 1", data=mtcars).fit(), lm("mpg ~ wt", data=mtcars).fit())
168
+ compare(model("mpg ~ 1", mtcars).fit(), model("mpg ~ wt", mtcars).fit())
165
169
 
166
170
  # Model objects are fitted in-place, so they're usable after compare()
167
- compact = lm("mpg ~ 1", data=mtcars)
168
- full = lm("mpg ~ wt", data=mtcars)
171
+ compact = model("mpg ~ 1", mtcars)
172
+ full = model("mpg ~ wt", mtcars)
169
173
  compare(compact, full)
170
- full.coef_ # Works - model was auto-fitted
174
+ full.params # Works - model was auto-fitted
171
175
 
172
176
  # glm example (deviance test)
173
177
  compare(
174
- glm("am ~ 1", data=mtcars, family="binomial"),
175
- glm("am ~ wt", data=mtcars, family="binomial"),
178
+ model("am ~ 1", mtcars, family="binomial"),
179
+ model("am ~ wt", mtcars, family="binomial"),
176
180
  )
177
181
 
178
182
  # lmer example (likelihood ratio test)
179
183
  compare(
180
- lmer("Reaction ~ Days + (1|Subject)", data=sleepstudy),
181
- lmer("Reaction ~ Days + (Days|Subject)", data=sleepstudy),
184
+ model("Reaction ~ Days + (1|Subject)", sleepstudy).fit(method="ML"),
185
+ model("Reaction ~ Days + (Days|Subject)", sleepstudy).fit(method="ML"),
182
186
  )
183
187
 
184
188
  # CV example (Nadeau-Bengio corrected)
185
189
  compare(
186
- lm("mpg ~ 1", data=mtcars),
187
- lm("mpg ~ wt", data=mtcars),
190
+ model("mpg ~ 1", mtcars),
191
+ model("mpg ~ wt", mtcars),
188
192
  method="cv", cv=5, seed=42,
189
193
  )
190
194
  ```
@@ -218,7 +222,6 @@ def compare(
218
222
 
219
223
  See Also:
220
224
  - lrt: Likelihood ratio test for mixed models
221
- - anova: Type I/II/III ANOVA tables
222
225
  """
223
226
  # Validate and auto-fit models (pass method and refit for REML check)
224
227
  models = validate_models(models, method=method, refit=refit)
@@ -66,7 +66,7 @@ def compute_cv_fold_score(
66
66
 
67
67
  # Predict on test data
68
68
  new_model.predict(newdata=test_data)
69
- y_pred = new_model.predictions["fitted"].to_numpy()
69
+ y_pred = new_model.predictions[Col.FITTED].to_numpy()
70
70
 
71
71
  # Get actual test values
72
72
  y_true = test_data[model._spec.response_var].to_numpy()
@@ -104,13 +104,13 @@ def compare_cv(
104
104
  Returns:
105
105
  DataFrame with columns:
106
106
  - model: Formula string
107
+ - PRE: Proportional reduction in error
108
+ - t_stat: Nadeau-Bengio corrected t-statistic
107
109
  - cv_score: Mean CV score (error)
108
110
  - cv_se: Standard error of CV score
109
111
  - diff: Difference from reference model (first model)
110
112
  - diff_se: Corrected standard error of difference
111
- - t_stat: Nadeau-Bengio corrected t-statistic
112
113
  - p_value: Two-sided p-value
113
- - PRE: Proportional reduction in error
114
114
 
115
115
  Notes:
116
116
  The Nadeau-Bengio correction accounts for the fact that k-fold CV
@@ -51,14 +51,23 @@ def compare_deviance(models: list[Any], test: str = "chisq") -> pl.DataFrame:
51
51
  test: Test type: "chisq" for chi-squared test (default), "f" for F-test.
52
52
 
53
53
  Returns:
54
- DataFrame with columns:
54
+ DataFrame with columns (test="chisq"):
55
55
  - model: Formula string
56
- - df_resid: Residual degrees of freedom
56
+ - chi2: Chi-squared statistic (deviance difference)
57
+ - dev_diff: Deviance difference (reduction)
57
58
  - deviance: Residual deviance
58
59
  - df: Degrees of freedom for this comparison
60
+ - df_resid: Residual degrees of freedom
61
+ - p_value: p-value from chi-squared distribution
62
+
63
+ DataFrame with columns (test="f"):
64
+ - model: Formula string
65
+ - F: F-statistic
59
66
  - dev_diff: Deviance difference (reduction)
60
- - statistic: Chi-squared or F statistic
61
- - p_value: p-value from chi-squared or F distribution
67
+ - deviance: Residual deviance
68
+ - df: Degrees of freedom for this comparison
69
+ - df_resid: Residual degrees of freedom
70
+ - p_value: p-value from F distribution
62
71
 
63
72
  Notes:
64
73
  The deviance difference follows a chi-squared distribution with df degrees
@@ -6,11 +6,7 @@ used across all comparison strategies (F-test, deviance, LRT, CV).
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- from typing import TYPE_CHECKING, Any
10
-
11
-
12
- if TYPE_CHECKING:
13
- pass
9
+ from typing import Any
14
10
 
15
11
  __all__ = [
16
12
  "check_nested",
@@ -154,13 +150,12 @@ def sort_models_by_complexity(models: tuple[Any, ...]) -> list[Any]:
154
150
  def resolve_method(model: Any) -> str:
155
151
  """Infer the appropriate comparison method for a model type.
156
152
 
157
- Uses :func:`classify_model_type` to canonically determine the model type
158
- from its family and random-effects structure, then maps to the
159
- corresponding comparison method.
153
+ Reads ``model._model_type`` to determine the model type and maps it
154
+ to the corresponding comparison method.
160
155
 
161
156
  Args:
162
- model: A fitted model object with a ``_spec`` attribute containing
163
- ``family`` and ``has_random_effects``.
157
+ model: A fitted model object with a ``_model_type`` attribute
158
+ (one of ``"lm"``, ``"glm"``, ``"lmer"``, ``"glmer"``).
164
159
 
165
160
  Returns:
166
161
  Comparison method: ``"f"`` for lm, ``"lrt"`` for lmer/glmer,