lme-python 0.1.4__tar.gz → 0.1.7__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 (175) hide show
  1. lme_python-0.1.7/.github/workflows/audit.yml +73 -0
  2. {lme_python-0.1.4 → lme_python-0.1.7}/.github/workflows/benchmarks.yml +0 -61
  3. lme_python-0.1.7/.github/workflows/ci.yml +225 -0
  4. lme_python-0.1.7/.github/workflows/crate-publish-dry-run.yml +28 -0
  5. lme_python-0.1.7/.github/workflows/fuzz-smoke.yml +34 -0
  6. {lme_python-0.1.4 → lme_python-0.1.7}/.github/workflows/python-release.yml +12 -12
  7. {lme_python-0.1.4 → lme_python-0.1.7}/.github/workflows/repo-metadata.yml +1 -1
  8. {lme_python-0.1.4 → lme_python-0.1.7}/.gitignore +3 -0
  9. {lme_python-0.1.4 → lme_python-0.1.7}/BENCHMARKS.md +1 -1
  10. {lme_python-0.1.4 → lme_python-0.1.7}/CHANGELOG.md +31 -0
  11. lme_python-0.1.7/CONTRIBUTING.md +122 -0
  12. {lme_python-0.1.4 → lme_python-0.1.7}/Cargo.lock +11 -11
  13. {lme_python-0.1.4 → lme_python-0.1.7}/Cargo.toml +22 -1
  14. {lme_python-0.1.4 → lme_python-0.1.7}/GUIDE.md +11 -8
  15. {lme_python-0.1.4 → lme_python-0.1.7}/PKG-INFO +5 -1
  16. {lme_python-0.1.4 → lme_python-0.1.7}/README.md +9 -5
  17. {lme_python-0.1.4 → lme_python-0.1.7}/RELEASING.md +24 -1
  18. lme_python-0.1.7/REPO_COMPLETION_BY_AREA.md +74 -0
  19. lme_python-0.1.7/benches/bench_load_production.rs +188 -0
  20. {lme_python-0.1.4 → lme_python-0.1.7}/benches/bench_math.rs +41 -0
  21. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/COMPARISONS.md +309 -78
  22. lme_python-0.1.7/comparisons/categorical_anova.R +17 -0
  23. lme_python-0.1.7/comparisons/categorical_anova.jl +13 -0
  24. lme_python-0.1.7/comparisons/categorical_anova.py +16 -0
  25. lme_python-0.1.7/comparisons/categorical_anova.rs +24 -0
  26. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_cbpp.R +15 -5
  27. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_cbpp.jl +3 -0
  28. lme_python-0.1.7/comparisons/glmm_cbpp.py +66 -0
  29. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_cbpp.rs +8 -4
  30. lme_python-0.1.7/comparisons/glmm_gaussian.R +29 -0
  31. lme_python-0.1.7/comparisons/glmm_gaussian.jl +29 -0
  32. lme_python-0.1.7/comparisons/glmm_gaussian.py +54 -0
  33. lme_python-0.1.7/comparisons/glmm_gaussian.rs +46 -0
  34. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_grouseticks.rs +1 -1
  35. lme_python-0.1.7/comparisons/lmer_weighted.R +28 -0
  36. lme_python-0.1.7/comparisons/lmer_weighted.jl +37 -0
  37. lme_python-0.1.7/comparisons/lmer_weighted.py +63 -0
  38. lme_python-0.1.7/comparisons/lmer_weighted.rs +51 -0
  39. lme_python-0.1.7/comparisons/lmm_dyestuff.py +33 -0
  40. lme_python-0.1.7/comparisons/lmm_pastes.py +47 -0
  41. lme_python-0.1.7/comparisons/lmm_penicillin.py +43 -0
  42. {lme_python-0.1.4 → lme_python-0.1.7}/pyproject.toml +7 -0
  43. {lme_python-0.1.4 → lme_python-0.1.7}/python/Cargo.lock +22 -21
  44. {lme_python-0.1.4 → lme_python-0.1.7}/python/Cargo.toml +3 -2
  45. {lme_python-0.1.4 → lme_python-0.1.7}/python/PYTHON_GUIDE.md +3 -2
  46. lme_python-0.1.7/python/examples/plotting_demo/.gitignore +6 -0
  47. lme_python-0.1.7/python/examples/plotting_demo/README.md +129 -0
  48. lme_python-0.1.7/python/examples/plotting_demo/compare_plots.py +310 -0
  49. lme_python-0.1.7/python/examples/plotting_demo/figure_specs.py +45 -0
  50. lme_python-0.1.7/python/examples/plotting_demo/find_rscript.py +92 -0
  51. lme_python-0.1.7/python/examples/plotting_demo/numeric_overlay.py +143 -0
  52. lme_python-0.1.7/python/examples/plotting_demo/paths.py +14 -0
  53. lme_python-0.1.7/python/examples/plotting_demo/plot_demo.py +185 -0
  54. lme_python-0.1.7/python/examples/plotting_demo/plot_r.R +177 -0
  55. lme_python-0.1.7/python/examples/plotting_demo/run_all.py +58 -0
  56. lme_python-0.1.7/python/examples/verification_project/README.md +42 -0
  57. lme_python-0.1.7/python/examples/verification_project/conftest.py +8 -0
  58. lme_python-0.1.7/python/examples/verification_project/parity.py +197 -0
  59. lme_python-0.1.7/python/examples/verification_project/paths.py +14 -0
  60. lme_python-0.1.7/python/examples/verification_project/run.py +39 -0
  61. lme_python-0.1.7/python/examples/verification_project/test_parity.py +41 -0
  62. lme_python-0.1.7/python/requirements-ci.txt +87 -0
  63. {lme_python-0.1.4 → lme_python-0.1.7}/python/src/lib.rs +39 -6
  64. lme_python-0.1.7/scripts/local_ci.ps1 +85 -0
  65. lme_python-0.1.7/scripts/local_ci.sh +86 -0
  66. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/run_cross_language_benchmarks.py +19 -1
  67. lme_python-0.1.7/src/anova.rs +398 -0
  68. lme_python-0.1.7/src/anova_contrasts.rs +208 -0
  69. lme_python-0.1.7/src/ddf.rs +196 -0
  70. {lme_python-0.1.4 → lme_python-0.1.7}/src/formula.rs +21 -0
  71. lme_python-0.1.7/src/glmm_math.rs +1217 -0
  72. {lme_python-0.1.4 → lme_python-0.1.7}/src/lib.rs +198 -27
  73. {lme_python-0.1.4 → lme_python-0.1.7}/src/model_matrix.rs +255 -14
  74. {lme_python-0.1.4 → lme_python-0.1.7}/src/optimizer.rs +88 -64
  75. {lme_python-0.1.4 → lme_python-0.1.7}/src/robust.rs +8 -2
  76. {lme_python-0.1.4 → lme_python-0.1.7}/src/satterthwaite.rs +32 -22
  77. lme_python-0.1.7/tests/categorical_anova_test.rs +42 -0
  78. lme_python-0.1.7/tests/data/golden_parity_manifest.json +440 -0
  79. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/intercept_only.json +2 -0
  80. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/mock_ml.json +2 -0
  81. lme_python-0.1.7/tests/data/pastes_cask_reml.json +13 -0
  82. lme_python-0.1.7/tests/data/penicillin.csv +145 -0
  83. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/penicillin.json +2 -0
  84. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/random_slopes.json +2 -0
  85. lme_python-0.1.7/tests/generate_test_data.R +506 -0
  86. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_anova.rs +6 -6
  87. lme_python-0.1.7/tests/test_anova_types.rs +56 -0
  88. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_confint_simulate.rs +13 -2
  89. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_coverage_edge_cases.rs +2 -1
  90. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_coverage_gaps.rs +3 -0
  91. lme_python-0.1.7/tests/test_failure_modes.rs +222 -0
  92. lme_python-0.1.7/tests/test_formula_stress.rs +316 -0
  93. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_gaps.rs +10 -3
  94. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_glmm.rs +96 -3
  95. lme_python-0.1.7/tests/test_golden_parity.rs +543 -0
  96. lme_python-0.1.7/tests/test_production_load.rs +360 -0
  97. lme_python-0.1.7/tests/test_statistical_identities.rs +294 -0
  98. lme_python-0.1.4/.github/workflows/ci.yml +0 -66
  99. lme_python-0.1.4/CONTRIBUTING.md +0 -91
  100. lme_python-0.1.4/comparisons/glmm_cbpp.py +0 -45
  101. lme_python-0.1.4/comparisons/lmm_dyestuff.py +0 -21
  102. lme_python-0.1.4/comparisons/lmm_pastes.py +0 -35
  103. lme_python-0.1.4/comparisons/lmm_penicillin.py +0 -35
  104. lme_python-0.1.4/src/anova.rs +0 -164
  105. lme_python-0.1.4/src/glmm_math.rs +0 -606
  106. lme_python-0.1.4/tests/data/penicillin.csv +0 -145
  107. lme_python-0.1.4/tests/generate_test_data.R +0 -231
  108. {lme_python-0.1.4 → lme_python-0.1.7}/LICENSE +0 -0
  109. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/comparison_chart.png +0 -0
  110. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_grouseticks.R +0 -0
  111. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_grouseticks.jl +0 -0
  112. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/glmm_grouseticks.py +0 -0
  113. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_dyestuff.R +0 -0
  114. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_dyestuff.jl +0 -0
  115. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_dyestuff.rs +0 -0
  116. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_pastes.R +0 -0
  117. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_pastes.jl +0 -0
  118. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_pastes.rs +0 -0
  119. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_penicillin.R +0 -0
  120. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_penicillin.jl +0 -0
  121. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/lmm_penicillin.rs +0 -0
  122. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy.R +0 -0
  123. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy.jl +0 -0
  124. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy.py +0 -0
  125. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy.rs +0 -0
  126. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy_ml.R +0 -0
  127. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy_ml.jl +0 -0
  128. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy_ml.py +0 -0
  129. {lme_python-0.1.4 → lme_python-0.1.7}/comparisons/sleepstudy_ml.rs +0 -0
  130. {lme_python-0.1.4 → lme_python-0.1.7}/docs/benchmarks/app.js +0 -0
  131. {lme_python-0.1.4 → lme_python-0.1.7}/docs/benchmarks/index.html +0 -0
  132. {lme_python-0.1.4 → lme_python-0.1.7}/docs/benchmarks/styles.css +0 -0
  133. {lme_python-0.1.4 → lme_python-0.1.7}/docs/index.html +0 -0
  134. {lme_python-0.1.4 → lme_python-0.1.7}/python/examples/glmer_cbpp.py +0 -0
  135. {lme_python-0.1.4 → lme_python-0.1.7}/python/examples/glmer_grouseticks.py +0 -0
  136. {lme_python-0.1.4 → lme_python-0.1.7}/python/examples/lm_basics.py +0 -0
  137. {lme_python-0.1.4 → lme_python-0.1.7}/python/examples/lmer_sleepstudy.py +0 -0
  138. {lme_python-0.1.4 → lme_python-0.1.7}/python/examples/model_comparison.py +0 -0
  139. {lme_python-0.1.4 → lme_python-0.1.7}/python/tests/test_basic.py +0 -0
  140. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/ast_explorations/test_fiasto.rs +0 -0
  141. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
  142. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/build_benchmark_site.py +0 -0
  143. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/dump_dyestuff.R +0 -0
  144. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/dump_pastes.R +0 -0
  145. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/make_penicillin_csv.py +0 -0
  146. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/plot_comparisons.py +0 -0
  147. {lme_python-0.1.4 → lme_python-0.1.7}/scripts/sync_github_repo_metadata.py +0 -0
  148. {lme_python-0.1.4 → lme_python-0.1.7}/src/family.rs +0 -0
  149. {lme_python-0.1.4 → lme_python-0.1.7}/src/kenward_roger.rs +0 -0
  150. {lme_python-0.1.4 → lme_python-0.1.7}/src/math.rs +0 -0
  151. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/cbpp_binary.csv +0 -0
  152. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/dyestuff.csv +0 -0
  153. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/glmm_binomial.json +0 -0
  154. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/glmm_poisson.json +0 -0
  155. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/grouseticks.csv +0 -0
  156. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/mock_crossed.json +0 -0
  157. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/pastes.csv +0 -0
  158. {lme_python-0.1.4 → lme_python-0.1.7}/tests/data/sleepstudy.csv +0 -0
  159. {lme_python-0.1.4 → lme_python-0.1.7}/tests/generate_mock_data.py +0 -0
  160. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_conditional_real.rs +0 -0
  161. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_crossed_mock.rs +0 -0
  162. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_e2e_lmer.rs +0 -0
  163. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_edge_cases.rs +0 -0
  164. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_errors.rs +0 -0
  165. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_formula.rs +0 -0
  166. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_intercept_only.rs +0 -0
  167. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_kenward_roger.rs +0 -0
  168. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_ml_optimization.rs +0 -0
  169. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_no_intercept.rs +0 -0
  170. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_numerical_parity.rs +0 -0
  171. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_predict.rs +0 -0
  172. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_random_slopes.rs +0 -0
  173. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_robust.rs +0 -0
  174. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_sandwich_math.R +0 -0
  175. {lme_python-0.1.4 → lme_python-0.1.7}/tests/test_satterthwaite.rs +0 -0
@@ -0,0 +1,73 @@
1
+ name: Security audit
2
+
3
+ on:
4
+ schedule:
5
+ # Weekly: Mondays 12:00 UTC
6
+ - cron: "0 12 * * 1"
7
+ workflow_dispatch:
8
+ push:
9
+ branches: [master]
10
+ paths:
11
+ - "**/Cargo.toml"
12
+ - "**/Cargo.lock"
13
+ - "python/pyproject.toml"
14
+ - "python/requirements-ci.txt"
15
+ pull_request:
16
+ branches: [master]
17
+ paths:
18
+ - "**/Cargo.toml"
19
+ - "**/Cargo.lock"
20
+ - "python/pyproject.toml"
21
+ - "python/requirements-ci.txt"
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ concurrency:
27
+ group: audit-${{ github.workflow }}-${{ github.ref }}
28
+ cancel-in-progress: true
29
+
30
+ jobs:
31
+ audit:
32
+ name: cargo audit
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v6
36
+
37
+ - uses: dtolnay/rust-toolchain@stable
38
+
39
+ - name: Install cargo-audit
40
+ uses: taiki-e/install-action@v2
41
+ with:
42
+ tool: cargo-audit@0.22.1
43
+
44
+ - name: Run cargo audit (root)
45
+ run: cargo audit
46
+
47
+ - name: Run cargo audit (python/)
48
+ working-directory: python
49
+ run: cargo audit
50
+
51
+ pip-audit:
52
+ name: pip-audit (python lockfile)
53
+ runs-on: ubuntu-latest
54
+ steps:
55
+ - uses: actions/checkout@v6
56
+
57
+ - name: Set up Python
58
+ uses: actions/setup-python@v6
59
+ with:
60
+ python-version: "3.11"
61
+ cache: pip
62
+ cache-dependency-path: python/requirements-ci.txt
63
+
64
+ - name: Install locked CI dependencies
65
+ working-directory: python
66
+ run: |
67
+ set -euo pipefail
68
+ python -m pip install -U pip
69
+ python -m pip install -r requirements-ci.txt
70
+
71
+ - name: pip-audit
72
+ working-directory: python
73
+ run: python -m pip_audit
@@ -17,7 +17,6 @@ on:
17
17
 
18
18
  permissions:
19
19
  contents: write
20
- pages: write
21
20
  id-token: write
22
21
 
23
22
  env:
@@ -172,63 +171,3 @@ jobs:
172
171
  echo "Cross-language benchmark script failed with exit code ${{ steps.cross_language_run.outputs.status }}." >&2
173
172
  exit 1
174
173
 
175
- pages:
176
- name: Publish Benchmark Dashboard
177
- runs-on: ubuntu-latest
178
- needs:
179
- - criterion
180
- - cross-language
181
- if: needs.criterion.result == 'success' && needs.cross-language.result == 'success' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/'))
182
- environment:
183
- name: github-pages
184
- url: ${{ steps.deploy.outputs.page_url }}
185
- steps:
186
- - uses: actions/checkout@v6
187
-
188
- - uses: actions/setup-python@v6
189
- with:
190
- python-version: "3.11"
191
-
192
- - name: Download cross-language artifact
193
- uses: actions/download-artifact@v5
194
- with:
195
- name: cross-language-${{ github.sha }}
196
- path: site-input
197
-
198
- - name: Resolve release URL
199
- id: release_url
200
- run: |
201
- if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
202
- echo "value=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
203
- else
204
- echo "value=" >> "$GITHUB_OUTPUT"
205
- fi
206
-
207
- - name: Build benchmark dashboard
208
- run: |
209
- set -euo pipefail
210
- mkdir -p site
211
- cp -R docs/. site/
212
- mkdir -p site/benchmarks/data
213
- cross_json="$(find site-input -name 'cross-language-*.json' -print -quit)"
214
- python scripts/build_benchmark_site.py \
215
- --cross-language-json "$cross_json" \
216
- --output-dir "site/benchmarks/data" \
217
- --run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
218
- --release-url "${{ steps.release_url.outputs.value }}" \
219
- --criterion-asset-name "criterion-${GITHUB_SHA}.tar.gz" \
220
- --cross-language-asset-name "cross-language-${GITHUB_SHA}.json" \
221
- --ref-name "${GITHUB_REF_NAME}"
222
-
223
- - uses: actions/configure-pages@v5
224
- with:
225
- token: ${{ secrets.REPO_ADMIN_TOKEN != '' && secrets.REPO_ADMIN_TOKEN || github.token }}
226
- enablement: ${{ secrets.REPO_ADMIN_TOKEN != '' }}
227
-
228
- - uses: actions/upload-pages-artifact@v3
229
- with:
230
- path: site
231
-
232
- - name: Deploy to GitHub Pages
233
- id: deploy
234
- uses: actions/deploy-pages@v4
@@ -0,0 +1,225 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ paths-ignore:
7
+ - "**/*.md"
8
+ - "LICENSE"
9
+ pull_request:
10
+ branches: [master]
11
+ paths-ignore:
12
+ - "**/*.md"
13
+ - "LICENSE"
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ concurrency:
19
+ group: ci-${{ github.workflow }}-${{ github.ref }}
20
+ cancel-in-progress: true
21
+
22
+ env:
23
+ CARGO_TERM_COLOR: always
24
+
25
+ jobs:
26
+ test:
27
+ name: Test (${{ matrix.os }})
28
+ runs-on: ${{ matrix.os }}
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ os: [ubuntu-latest, windows-latest, macos-latest]
33
+
34
+ steps:
35
+ - uses: actions/checkout@v6
36
+
37
+ - name: Install Rust toolchain
38
+ uses: dtolnay/rust-toolchain@stable
39
+
40
+ - name: Install OpenBLAS (macOS)
41
+ if: matrix.os == 'macos-latest'
42
+ run: brew install openblas
43
+
44
+ - name: Install OpenBLAS (Ubuntu)
45
+ if: matrix.os == 'ubuntu-latest'
46
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
47
+
48
+ - name: Cache cargo registry & build
49
+ uses: actions/cache@v5
50
+ with:
51
+ path: |
52
+ ~/.cargo/registry
53
+ ~/.cargo/git
54
+ target
55
+ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
56
+ restore-keys: ${{ runner.os }}-cargo-
57
+
58
+ - name: Build
59
+ run: cargo build --verbose --locked
60
+
61
+ - name: Run tests
62
+ run: cargo test --verbose --locked
63
+
64
+ - name: Set up Python
65
+ uses: actions/setup-python@v6
66
+ with:
67
+ python-version: "3.11"
68
+ cache: pip
69
+ cache-dependency-path: python/requirements-ci.txt
70
+
71
+ - name: Python bindings (maturin develop, pytest, wheel install, pytest)
72
+ working-directory: python
73
+ shell: bash
74
+ run: |
75
+ set -euo pipefail
76
+ python -m venv .venv
77
+ if [ -f .venv/Scripts/python.exe ]; then
78
+ VPY=.venv/Scripts/python.exe
79
+ else
80
+ VPY=.venv/bin/python
81
+ fi
82
+ "$VPY" -m pip install -U pip
83
+ "$VPY" -m pip install -r requirements-ci.txt
84
+ "$VPY" -m maturin develop --release
85
+ "$VPY" -m pytest tests/ -v
86
+ "$VPY" -m maturin build --release -o dist
87
+ shopt -s nullglob
88
+ wheels=(dist/lme_python-*.whl)
89
+ if [ ${#wheels[@]} -eq 0 ]; then
90
+ echo "No wheel found under python/dist"
91
+ ls -la dist 2>/dev/null || true
92
+ exit 1
93
+ fi
94
+ "$VPY" -m pip install --force-reinstall "${wheels[0]}"
95
+ "$VPY" -m pytest tests/ -v
96
+
97
+ rust-all-targets:
98
+ name: Rust check (all targets)
99
+ runs-on: ubuntu-latest
100
+ steps:
101
+ - uses: actions/checkout@v6
102
+ - uses: dtolnay/rust-toolchain@stable
103
+ - name: Install OpenBLAS
104
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
105
+ - name: Cache cargo registry & build
106
+ uses: actions/cache@v5
107
+ with:
108
+ path: |
109
+ ~/.cargo/registry
110
+ ~/.cargo/git
111
+ target
112
+ key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
113
+ restore-keys: ubuntu-cargo-
114
+ - name: cargo check --workspace --all-targets
115
+ run: cargo check --workspace --all-targets --locked -v
116
+
117
+ production-load:
118
+ name: Production load & perf gates (ubuntu, release)
119
+ runs-on: ubuntu-latest
120
+ steps:
121
+ - uses: actions/checkout@v6
122
+
123
+ - name: Install Rust toolchain
124
+ uses: dtolnay/rust-toolchain@stable
125
+
126
+ - name: Install OpenBLAS (Ubuntu)
127
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
128
+
129
+ - name: Cache cargo registry & build
130
+ uses: actions/cache@v5
131
+ with:
132
+ path: |
133
+ ~/.cargo/registry
134
+ ~/.cargo/git
135
+ target
136
+ key: ubuntu-prodload-${{ hashFiles('**/Cargo.lock') }}
137
+ restore-keys: ubuntu-prodload-
138
+
139
+ - name: Production load tests (smoke + release wall-clock gates)
140
+ run: cargo test --release --locked --verbose --test test_production_load
141
+
142
+ - name: Heavy production load tests (ignored)
143
+ run: cargo test --release --locked --verbose --test test_production_load -- --ignored --test-threads=1
144
+
145
+ rust-doctests:
146
+ name: Rust doctests
147
+ runs-on: ubuntu-latest
148
+ steps:
149
+ - uses: actions/checkout@v6
150
+ - uses: dtolnay/rust-toolchain@stable
151
+ - name: Install OpenBLAS
152
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
153
+ - name: Cache cargo registry & build
154
+ uses: actions/cache@v5
155
+ with:
156
+ path: |
157
+ ~/.cargo/registry
158
+ ~/.cargo/git
159
+ target
160
+ key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
161
+ restore-keys: ubuntu-cargo-
162
+ - name: cargo test --doc
163
+ run: cargo test --doc --locked --verbose
164
+
165
+ python-bindings-versions:
166
+ name: Python ${{ matrix.python-version }} (ubuntu)
167
+ runs-on: ubuntu-latest
168
+ strategy:
169
+ fail-fast: false
170
+ matrix:
171
+ python-version: ["3.10", "3.12", "3.13"]
172
+ steps:
173
+ - uses: actions/checkout@v6
174
+ - uses: dtolnay/rust-toolchain@stable
175
+ - name: Install OpenBLAS
176
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
177
+ - name: Cache cargo registry & build
178
+ uses: actions/cache@v5
179
+ with:
180
+ path: |
181
+ ~/.cargo/registry
182
+ ~/.cargo/git
183
+ target
184
+ key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
185
+ restore-keys: ubuntu-cargo-
186
+ - name: Set up Python
187
+ uses: actions/setup-python@v6
188
+ with:
189
+ python-version: ${{ matrix.python-version }}
190
+ cache: pip
191
+ cache-dependency-path: python/requirements-ci.txt
192
+ - name: Python bindings (maturin develop, pytest, wheel install, pytest)
193
+ working-directory: python
194
+ shell: bash
195
+ run: |
196
+ set -euo pipefail
197
+ python -m venv .venv
198
+ if [ -f .venv/Scripts/python.exe ]; then
199
+ VPY=.venv/Scripts/python.exe
200
+ else
201
+ VPY=.venv/bin/python
202
+ fi
203
+ "$VPY" -m pip install -U pip
204
+ "$VPY" -m pip install -r requirements-ci.txt
205
+ "$VPY" -m maturin develop --release
206
+ "$VPY" -m pytest tests/ -v
207
+ "$VPY" -m maturin build --release -o dist
208
+ shopt -s nullglob
209
+ wheels=(dist/lme_python-*.whl)
210
+ if [ ${#wheels[@]} -eq 0 ]; then
211
+ echo "No wheel found under python/dist"
212
+ ls -la dist 2>/dev/null || true
213
+ exit 1
214
+ fi
215
+ "$VPY" -m pip install --force-reinstall "${wheels[0]}"
216
+ "$VPY" -m pytest tests/ -v
217
+
218
+ docs:
219
+ name: Build docs
220
+ runs-on: ubuntu-latest
221
+ steps:
222
+ - uses: actions/checkout@v6
223
+ - uses: dtolnay/rust-toolchain@stable
224
+ - name: Build documentation
225
+ run: cargo doc --no-deps --verbose --locked
@@ -0,0 +1,28 @@
1
+ name: Crate publish (dry-run)
2
+
3
+ # Verifies `cargo publish` packaging (metadata, README, excluded files) without uploading.
4
+
5
+ on:
6
+ push:
7
+ tags:
8
+ - "v*"
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: publish-dry-run-${{ github.workflow }}-${{ github.ref }}
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ dry-run:
20
+ name: cargo publish --dry-run
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+
25
+ - uses: dtolnay/rust-toolchain@stable
26
+
27
+ - name: Dry-run publish (root crate)
28
+ run: cargo publish --dry-run --locked
@@ -0,0 +1,34 @@
1
+ # Manual libFuzzer smoke (long compile + short run). Trigger from the Actions tab.
2
+ name: Fuzz (manual)
3
+
4
+ on:
5
+ workflow_dispatch:
6
+
7
+ jobs:
8
+ libfuzzer-smoke:
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 120
11
+ defaults:
12
+ run:
13
+ working-directory: fuzz
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - uses: dtolnay/rust-toolchain@nightly
19
+
20
+ - name: Install cargo-fuzz
21
+ run: cargo install cargo-fuzz --locked
22
+
23
+ - uses: Swatinem/rust-cache@v2
24
+ with:
25
+ workspaces: fuzz
26
+
27
+ - name: Build fuzz targets
28
+ run: cargo fuzz build formula_parse && cargo fuzz build formula_pipeline
29
+
30
+ - name: Run formula_parse
31
+ run: cargo fuzz run formula_parse corpus/formula_parse -- -runs=15000 -max_len=16384
32
+
33
+ - name: Run formula_pipeline
34
+ run: cargo fuzz run formula_pipeline corpus/formula_parse -- -runs=15000 -max_len=16384
@@ -23,8 +23,8 @@ jobs:
23
23
  matrix:
24
24
  target: [x86_64, aarch64]
25
25
  steps:
26
- - uses: actions/checkout@v4
27
- - uses: actions/setup-python@v5
26
+ - uses: actions/checkout@v6
27
+ - uses: actions/setup-python@v6
28
28
  with:
29
29
  python-version: '3.10'
30
30
  - name: Build wheels
@@ -44,7 +44,7 @@ jobs:
44
44
  CFLAGS: "-std=gnu99"
45
45
  CFLAGS_aarch64_unknown_linux_gnu: "-std=gnu99"
46
46
  - name: Upload wheels
47
- uses: actions/upload-artifact@v4
47
+ uses: actions/upload-artifact@v7
48
48
  with:
49
49
  name: wheels-linux-${{ matrix.target }}
50
50
  path: dist
@@ -55,8 +55,8 @@ jobs:
55
55
  matrix:
56
56
  target: [x64]
57
57
  steps:
58
- - uses: actions/checkout@v4
59
- - uses: actions/setup-python@v5
58
+ - uses: actions/checkout@v6
59
+ - uses: actions/setup-python@v6
60
60
  with:
61
61
  python-version: '3.10'
62
62
  architecture: ${{ matrix.target }}
@@ -67,7 +67,7 @@ jobs:
67
67
  args: --release --out dist --manifest-path python/Cargo.toml
68
68
  sccache: 'true'
69
69
  - name: Upload wheels
70
- uses: actions/upload-artifact@v4
70
+ uses: actions/upload-artifact@v7
71
71
  with:
72
72
  name: wheels-windows-${{ matrix.target }}
73
73
  path: dist
@@ -78,8 +78,8 @@ jobs:
78
78
  matrix:
79
79
  target: [x86_64, aarch64]
80
80
  steps:
81
- - uses: actions/checkout@v4
82
- - uses: actions/setup-python@v5
81
+ - uses: actions/checkout@v6
82
+ - uses: actions/setup-python@v6
83
83
  with:
84
84
  python-version: '3.10'
85
85
  - name: Install OpenBLAS
@@ -91,7 +91,7 @@ jobs:
91
91
  args: --release --out dist --manifest-path python/Cargo.toml
92
92
  sccache: 'true'
93
93
  - name: Upload wheels
94
- uses: actions/upload-artifact@v4
94
+ uses: actions/upload-artifact@v7
95
95
  with:
96
96
  name: wheels-macos-${{ matrix.target }}
97
97
  path: dist
@@ -99,14 +99,14 @@ jobs:
99
99
  sdist:
100
100
  runs-on: ubuntu-latest
101
101
  steps:
102
- - uses: actions/checkout@v4
102
+ - uses: actions/checkout@v6
103
103
  - name: Build sdist
104
104
  uses: PyO3/maturin-action@v1
105
105
  with:
106
106
  command: sdist
107
107
  args: --out dist --manifest-path python/Cargo.toml
108
108
  - name: Upload sdist
109
- uses: actions/upload-artifact@v4
109
+ uses: actions/upload-artifact@v7
110
110
  with:
111
111
  name: wheels-sdist
112
112
  path: dist
@@ -122,7 +122,7 @@ jobs:
122
122
  id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
123
123
  contents: write # needed to create GitHub Release
124
124
  steps:
125
- - uses: actions/download-artifact@v4
125
+ - uses: actions/download-artifact@v7
126
126
  with:
127
127
  path: dist
128
128
  pattern: wheels-*
@@ -16,7 +16,7 @@ jobs:
16
16
  sync:
17
17
  runs-on: ubuntu-latest
18
18
  steps:
19
- - uses: actions/checkout@v4
19
+ - uses: actions/checkout@v6
20
20
 
21
21
  - name: Verify admin token is configured
22
22
  env:
@@ -1,4 +1,7 @@
1
1
  /target
2
+ /fuzz/artifacts/
3
+ /fuzz/coverage/
4
+ /fuzz/target/
2
5
  reference/
3
6
  rlib/
4
7
  julia-portable/
@@ -15,6 +15,7 @@ It currently covers these benchmark families:
15
15
  - end-to-end `lmer()` fitting on `sleepstudy`
16
16
  - end-to-end weighted `lmer_weighted()` fitting on fixture-backed and large synthetic cases
17
17
  - end-to-end `glmer()` fitting on `grouseticks` and `cbpp`
18
+ - `glmm_agq_cbpp`: same CBPP binomial fit with `n_agq = 1` (Laplace) vs `n_agq = 7` (scalar AGQ), lower sample size because AGQ is expensive
18
19
  - population-level and conditional prediction on a larger repeated `sleepstudy` frame
19
20
  - response-scale and conditional response-scale GLMM prediction on repeated Poisson and Binomial frames
20
21
  - GLMM post-fit helpers such as Wald confidence intervals
@@ -122,7 +123,6 @@ The repository also includes a dedicated workflow in [.github/workflows/benchmar
122
123
  - runs the cross-language benchmark script
123
124
  - uploads the resulting artifacts in CI
124
125
  - attaches them to GitHub Releases on tag pushes
125
- - publishes the latest benchmark dashboard to GitHub Pages at `https://x4g4p3x.github.io/lme-rs/benchmarks/`
126
126
 
127
127
  ## Latest published results
128
128
 
@@ -5,6 +5,37 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.7] - 2026-05-18
11
+
12
+ ### Added
13
+
14
+ - **Type II** fixed-effects ANOVA via `LmeFit::anova_typed(AnovaType::Type2, ddf)` ([`src/anova_contrasts.rs`](src/anova_contrasts.rs), `lmerTest`-style Doolittle contrasts for contained terms). `anova(ddf)` remains Type III. Python: `fit.anova("satterthwaite", anova_type="II")`.
15
+ - Design-matrix support for **`FixedEffect`** / **`InteractionTerm`** fiasto roles and two-way categorical interactions (`y ~ a * b`).
16
+ - Golden parity case **`pastes_cask_multi_dof_reml`** in [`tests/data/golden_parity_manifest.json`](tests/data/golden_parity_manifest.json) (Type III Satterthwaite ANOVA for `cask` on the pastes dataset), with fixture [`tests/data/pastes_cask_reml.json`](tests/data/pastes_cask_reml.json) and R regeneration via [`tests/generate_test_data.R`](tests/generate_test_data.R).
17
+ - Numeric parity checks for categorical multi-DoF ANOVA in [`tests/categorical_anova_test.rs`](tests/categorical_anova_test.rs) and [`python/examples/verification_project/parity.py`](python/examples/verification_project/parity.py).
18
+
19
+ ### Changed
20
+
21
+ - Multi-DoF Type III ANOVA denominator df for Satterthwaite now follows **`lmerTest::contestMD()`** ([`src/ddf.rs`](src/ddf.rs)): eigen-decomposed Wald contrasts, per-direction Satterthwaite dfs, and `get_Fstat_ddf()` pooling (replacing `min()` of marginal dfs). Kenward–Roger multi-DoF rows use the same pooling on marginal dfs.
22
+
23
+ ## [0.1.6] - 2026-03-27
24
+
25
+ ### Added
26
+
27
+ - Python `examples/plotting_demo`: matplotlib figures from `lme_python`, optional **lme4** parity (`plot_r.R`), side-by-side PNGs (`figures_compare/`), numeric overlay on shared axes (`figures_overlay/` via `figures_data/` JSON/CSV), `run_all.py`, Windows `Rscript` discovery (`find_rscript.py`), and raster fallback (`figures_overlay_raster/`).
28
+ - `scripts/local_ci.sh` and `scripts/local_ci.ps1` to run the same checks as [`.github/workflows/ci.yml`](.github/workflows/ci.yml) locally (`fmt`, `clippy`, build, test, `doc`).
29
+
30
+ ## [0.1.5] - 2026-03-22
31
+
32
+ ### Added
33
+
34
+ - Added automatic categorical dummy encoding (dropping collinear intercepts) directly from categorical/string Polars dataframe columns natively.
35
+ - Implemented Multi-DoF Joint Wald F-Tests tracking dummy groups in `anova()` (Type III Satterthwaite representations) for valid categorical fixed-effect testing.
36
+ - Added support for generating `Gaussian` families in Python's `glmer` mapping, alongside exposing an equivalent `lmer_weighted` function.
37
+ - Plumbed explicit Adaptive Gauss-Hermite Quadrature (`nAGQ`) scaling configuration arrays throughout the optimizer targeting upcoming structural convergence capabilities.
38
+
8
39
  ## [0.1.4] - 2026-03-22
9
40
 
10
41
  ### Added