lme-python 0.1.3__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 (178) hide show
  1. lme_python-0.1.7/.github/workflows/audit.yml +73 -0
  2. {lme_python-0.1.3 → lme_python-0.1.7}/.github/workflows/benchmarks.yml +2 -0
  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.3 → lme_python-0.1.7}/.github/workflows/python-release.yml +12 -12
  7. {lme_python-0.1.3 → lme_python-0.1.7}/.github/workflows/repo-metadata.yml +1 -1
  8. {lme_python-0.1.3 → lme_python-0.1.7}/.gitignore +5 -0
  9. {lme_python-0.1.3 → lme_python-0.1.7}/BENCHMARKS.md +29 -1
  10. lme_python-0.1.7/CHANGELOG.md +103 -0
  11. lme_python-0.1.7/CONTRIBUTING.md +122 -0
  12. {lme_python-0.1.3 → lme_python-0.1.7}/Cargo.lock +11 -11
  13. {lme_python-0.1.3 → lme_python-0.1.7}/Cargo.toml +50 -1
  14. {lme_python-0.1.3 → lme_python-0.1.7}/GUIDE.md +16 -13
  15. {lme_python-0.1.3 → lme_python-0.1.7}/PKG-INFO +5 -1
  16. {lme_python-0.1.3 → lme_python-0.1.7}/README.md +22 -11
  17. {lme_python-0.1.3 → lme_python-0.1.7}/RELEASING.md +30 -7
  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.3 → lme_python-0.1.7}/benches/bench_math.rs +41 -0
  21. {lme_python-0.1.3/examples → 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.3/examples → lme_python-0.1.7/comparisons}/glmm_cbpp.R +15 -5
  27. {lme_python-0.1.3/examples → 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.3/examples → 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.3/examples → 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.7/docs/benchmarks/app.js +207 -0
  43. lme_python-0.1.7/docs/benchmarks/index.html +92 -0
  44. lme_python-0.1.7/docs/benchmarks/styles.css +305 -0
  45. lme_python-0.1.7/docs/index.html +63 -0
  46. {lme_python-0.1.3 → lme_python-0.1.7}/pyproject.toml +7 -0
  47. {lme_python-0.1.3 → lme_python-0.1.7}/python/Cargo.lock +22 -21
  48. {lme_python-0.1.3 → lme_python-0.1.7}/python/Cargo.toml +3 -2
  49. {lme_python-0.1.3 → lme_python-0.1.7}/python/PYTHON_GUIDE.md +63 -2
  50. lme_python-0.1.7/python/examples/glmer_cbpp.py +92 -0
  51. lme_python-0.1.7/python/examples/glmer_grouseticks.py +99 -0
  52. lme_python-0.1.7/python/examples/lm_basics.py +84 -0
  53. lme_python-0.1.7/python/examples/lmer_sleepstudy.py +96 -0
  54. lme_python-0.1.7/python/examples/model_comparison.py +117 -0
  55. lme_python-0.1.7/python/examples/plotting_demo/.gitignore +6 -0
  56. lme_python-0.1.7/python/examples/plotting_demo/README.md +129 -0
  57. lme_python-0.1.7/python/examples/plotting_demo/compare_plots.py +310 -0
  58. lme_python-0.1.7/python/examples/plotting_demo/figure_specs.py +45 -0
  59. lme_python-0.1.7/python/examples/plotting_demo/find_rscript.py +92 -0
  60. lme_python-0.1.7/python/examples/plotting_demo/numeric_overlay.py +143 -0
  61. lme_python-0.1.7/python/examples/plotting_demo/paths.py +14 -0
  62. lme_python-0.1.7/python/examples/plotting_demo/plot_demo.py +185 -0
  63. lme_python-0.1.7/python/examples/plotting_demo/plot_r.R +177 -0
  64. lme_python-0.1.7/python/examples/plotting_demo/run_all.py +58 -0
  65. lme_python-0.1.7/python/examples/verification_project/README.md +42 -0
  66. lme_python-0.1.7/python/examples/verification_project/conftest.py +8 -0
  67. lme_python-0.1.7/python/examples/verification_project/parity.py +197 -0
  68. lme_python-0.1.7/python/examples/verification_project/paths.py +14 -0
  69. lme_python-0.1.7/python/examples/verification_project/run.py +39 -0
  70. lme_python-0.1.7/python/examples/verification_project/test_parity.py +41 -0
  71. lme_python-0.1.7/python/requirements-ci.txt +87 -0
  72. lme_python-0.1.7/python/src/lib.rs +675 -0
  73. lme_python-0.1.7/python/tests/test_basic.py +406 -0
  74. lme_python-0.1.7/scripts/build_benchmark_site.py +210 -0
  75. lme_python-0.1.7/scripts/local_ci.ps1 +85 -0
  76. lme_python-0.1.7/scripts/local_ci.sh +86 -0
  77. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/plot_comparisons.py +2 -2
  78. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/run_cross_language_benchmarks.py +31 -13
  79. lme_python-0.1.7/src/anova.rs +398 -0
  80. lme_python-0.1.7/src/anova_contrasts.rs +208 -0
  81. lme_python-0.1.7/src/ddf.rs +196 -0
  82. {lme_python-0.1.3 → lme_python-0.1.7}/src/family.rs +2 -2
  83. {lme_python-0.1.3 → lme_python-0.1.7}/src/formula.rs +21 -0
  84. lme_python-0.1.7/src/glmm_math.rs +1217 -0
  85. {lme_python-0.1.3 → lme_python-0.1.7}/src/lib.rs +384 -33
  86. {lme_python-0.1.3 → lme_python-0.1.7}/src/model_matrix.rs +255 -14
  87. {lme_python-0.1.3 → lme_python-0.1.7}/src/optimizer.rs +88 -64
  88. {lme_python-0.1.3 → lme_python-0.1.7}/src/robust.rs +8 -2
  89. {lme_python-0.1.3 → lme_python-0.1.7}/src/satterthwaite.rs +32 -22
  90. lme_python-0.1.7/tests/categorical_anova_test.rs +42 -0
  91. lme_python-0.1.7/tests/data/golden_parity_manifest.json +440 -0
  92. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/intercept_only.json +2 -0
  93. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/mock_ml.json +2 -0
  94. lme_python-0.1.7/tests/data/pastes_cask_reml.json +13 -0
  95. lme_python-0.1.7/tests/data/penicillin.csv +145 -0
  96. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/penicillin.json +2 -0
  97. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/random_slopes.json +2 -0
  98. lme_python-0.1.7/tests/generate_test_data.R +506 -0
  99. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_anova.rs +6 -6
  100. lme_python-0.1.7/tests/test_anova_types.rs +56 -0
  101. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_confint_simulate.rs +62 -1
  102. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_coverage_edge_cases.rs +2 -1
  103. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_coverage_gaps.rs +3 -0
  104. lme_python-0.1.7/tests/test_failure_modes.rs +222 -0
  105. lme_python-0.1.7/tests/test_formula_stress.rs +316 -0
  106. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_gaps.rs +10 -3
  107. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_glmm.rs +140 -0
  108. lme_python-0.1.7/tests/test_golden_parity.rs +543 -0
  109. lme_python-0.1.7/tests/test_production_load.rs +360 -0
  110. lme_python-0.1.7/tests/test_statistical_identities.rs +294 -0
  111. lme_python-0.1.3/.github/workflows/ci.yml +0 -66
  112. lme_python-0.1.3/CHANGELOG.md +0 -60
  113. lme_python-0.1.3/CONTRIBUTING.md +0 -91
  114. lme_python-0.1.3/examples/glmm_cbpp.py +0 -45
  115. lme_python-0.1.3/examples/lmm_dyestuff.py +0 -21
  116. lme_python-0.1.3/examples/lmm_pastes.py +0 -35
  117. lme_python-0.1.3/examples/lmm_penicillin.py +0 -35
  118. lme_python-0.1.3/python/src/lib.rs +0 -201
  119. lme_python-0.1.3/python/tests/test_basic.py +0 -164
  120. lme_python-0.1.3/src/anova.rs +0 -164
  121. lme_python-0.1.3/src/glmm_math.rs +0 -558
  122. lme_python-0.1.3/tests/data/penicillin.csv +0 -145
  123. lme_python-0.1.3/tests/generate_test_data.R +0 -231
  124. {lme_python-0.1.3 → lme_python-0.1.7}/LICENSE +0 -0
  125. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/comparison_chart.png +0 -0
  126. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/glmm_grouseticks.R +0 -0
  127. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/glmm_grouseticks.jl +0 -0
  128. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/glmm_grouseticks.py +0 -0
  129. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_dyestuff.R +0 -0
  130. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_dyestuff.jl +0 -0
  131. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_dyestuff.rs +0 -0
  132. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_pastes.R +0 -0
  133. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_pastes.jl +0 -0
  134. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_pastes.rs +0 -0
  135. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_penicillin.R +0 -0
  136. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_penicillin.jl +0 -0
  137. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/lmm_penicillin.rs +0 -0
  138. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy.R +0 -0
  139. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy.jl +0 -0
  140. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy.py +0 -0
  141. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy.rs +0 -0
  142. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy_ml.R +0 -0
  143. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy_ml.jl +0 -0
  144. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy_ml.py +0 -0
  145. {lme_python-0.1.3/examples → lme_python-0.1.7/comparisons}/sleepstudy_ml.rs +0 -0
  146. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/ast_explorations/test_fiasto.rs +0 -0
  147. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
  148. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/dump_dyestuff.R +0 -0
  149. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/dump_pastes.R +0 -0
  150. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/make_penicillin_csv.py +0 -0
  151. {lme_python-0.1.3 → lme_python-0.1.7}/scripts/sync_github_repo_metadata.py +0 -0
  152. {lme_python-0.1.3 → lme_python-0.1.7}/src/kenward_roger.rs +0 -0
  153. {lme_python-0.1.3 → lme_python-0.1.7}/src/math.rs +0 -0
  154. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/cbpp_binary.csv +0 -0
  155. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/dyestuff.csv +0 -0
  156. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/glmm_binomial.json +0 -0
  157. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/glmm_poisson.json +0 -0
  158. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/grouseticks.csv +0 -0
  159. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/mock_crossed.json +0 -0
  160. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/pastes.csv +0 -0
  161. {lme_python-0.1.3 → lme_python-0.1.7}/tests/data/sleepstudy.csv +0 -0
  162. {lme_python-0.1.3 → lme_python-0.1.7}/tests/generate_mock_data.py +0 -0
  163. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_conditional_real.rs +0 -0
  164. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_crossed_mock.rs +0 -0
  165. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_e2e_lmer.rs +0 -0
  166. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_edge_cases.rs +0 -0
  167. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_errors.rs +0 -0
  168. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_formula.rs +0 -0
  169. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_intercept_only.rs +0 -0
  170. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_kenward_roger.rs +0 -0
  171. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_ml_optimization.rs +0 -0
  172. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_no_intercept.rs +0 -0
  173. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_numerical_parity.rs +0 -0
  174. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_predict.rs +0 -0
  175. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_random_slopes.rs +0 -0
  176. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_robust.rs +0 -0
  177. {lme_python-0.1.3 → lme_python-0.1.7}/tests/test_sandwich_math.R +0 -0
  178. {lme_python-0.1.3 → 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,6 +17,7 @@ on:
17
17
 
18
18
  permissions:
19
19
  contents: write
20
+ id-token: write
20
21
 
21
22
  env:
22
23
  CARGO_TERM_COLOR: always
@@ -169,3 +170,4 @@ jobs:
169
170
  run: |
170
171
  echo "Cross-language benchmark script failed with exit code ${{ steps.cross_language_run.outputs.status }}." >&2
171
172
  exit 1
173
+
@@ -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/
@@ -6,6 +9,8 @@ julia-portable.zip
6
9
  flamegraph.svg
7
10
  artifacts/
8
11
  benchmark-results/
12
+ benchmark-site/
13
+ site/
9
14
 
10
15
  # Python bindings
11
16
  python/target/
@@ -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
@@ -82,7 +83,7 @@ Recommended rules:
82
83
  - note when optimizers are not directly comparable across ecosystems
83
84
  - keep output artifacts or raw logs so later releases can be compared to the same baseline
84
85
 
85
- For this repo, the most defensible cross-language benchmark set would start with the same reference datasets already used in `examples/COMPARISONS.md`:
86
+ For this repo, the most defensible cross-language benchmark set would start with the same reference datasets already used in `comparisons/COMPARISONS.md`:
86
87
 
87
88
  - `sleepstudy`
88
89
  - `dyestuff`
@@ -123,6 +124,33 @@ The repository also includes a dedicated workflow in [.github/workflows/benchmar
123
124
  - uploads the resulting artifacts in CI
124
125
  - attaches them to GitHub Releases on tag pushes
125
126
 
127
+ ## Latest published results
128
+
129
+ The latest published benchmark artifacts are attached to the [v0.1.3 release](https://github.com/x4g4p3x/lme-rs/releases/tag/v0.1.3).
130
+
131
+ That release currently includes:
132
+
133
+ - `criterion-e8a82e0b97b88c5549bc61e89c22dc12c9060a02.tar.gz`
134
+ - `cross-language-e8a82e0b97b88c5549bc61e89c22dc12c9060a02.json`
135
+
136
+ The cross-language JSON includes representative timings for:
137
+
138
+ - `sleepstudy`
139
+ - `pastes`
140
+ - `cbpp`
141
+ - `grouseticks`
142
+
143
+ across:
144
+
145
+ - Rust
146
+ - Python
147
+ - R
148
+ - Julia
149
+
150
+ Treat those numbers as versioned release artifacts, not as universal constants. They were produced on GitHub-hosted runners with the workflow's pinned toolchain setup, so they are useful for release-to-release comparison and public transparency, but not as machine-independent proof of absolute speed.
151
+
152
+ If you want to cite benchmark results in release notes or external docs, prefer linking the release asset directly instead of copying raw numbers into the README. That keeps the landing page stable while still making the measured outputs inspectable.
153
+
126
154
  ## How to interpret results
127
155
 
128
156
  Use the existing suite primarily for:
@@ -0,0 +1,103 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
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
+
39
+ ## [0.1.4] - 2026-03-22
40
+
41
+ ### Added
42
+
43
+ - Formula-string entry point `lm()` and corresponding Python binding for fitting ordinary least squares models using DataFrames.
44
+ - New Python examples directory `python/examples` with five runnable demonstration scripts mapping to common mixed-modeling workflows.
45
+
46
+ ### Changed
47
+
48
+ - Renamed `examples/` directory to `comparisons/` to clarify that it contains cross-language parity scripts, updating all build and documentation references accordingly.
49
+ - Promoted Kenward-Roger degrees of freedom from "provisional" status after validating numerical parity (within 0.01 df) against R's `pbkrtest`.
50
+
51
+ ## [0.1.3] - 2026-03-12
52
+
53
+ ### Added
54
+
55
+ - Expanded the benchmark suite to cover parsing, matrix construction, prediction paths, inference helpers, weighted fits, Kenward-Roger timing, and size sweeps.
56
+ - Added automated cross-language benchmark scripting and a GitHub workflow that saves benchmark artifacts in CI and on release tags.
57
+ - Added `CONTRIBUTING.md`, `RELEASING.md`, and `BENCHMARKS.md` to document contributor setup, release flow, and benchmark methodology.
58
+
59
+ ### Changed
60
+
61
+ - Reworked the README, Rust guide, Python guide, and comparison documentation to better reflect current capabilities, limitations, and release workflow.
62
+ - Switched the crate homepage to the docs.rs site and expanded repository metadata sync for GitHub description, topics, and website fields.
63
+ - Hardened the benchmark automation and example scripts so cross-language benchmark runs complete consistently on GitHub Actions.
64
+
65
+ ## [0.1.2] - 2026-03-09
66
+
67
+ ### Fixed
68
+
69
+ - Fixed a NaN deviance issue in the fitting path.
70
+ - Expanded Python test coverage for the 0.1.2 release.
71
+
72
+ ### Changed
73
+
74
+ - Aligned `Cargo.lock` with the 0.1.2 version bump.
75
+
76
+ ## [0.1.1] - 2026-03-09
77
+
78
+ ### Changed
79
+
80
+ - Bumped the crate and Python extension versions for the 0.1.1 release.
81
+ - Updated lockfiles to remove yanked dependencies.
82
+ - Fixed missing documentation warnings needed for publishing.
83
+
84
+ ### Fixed
85
+
86
+ - Hardened the GitHub Actions and wheel build pipeline across Linux, macOS, and Windows.
87
+ - Restored missing dependencies and build flags needed for OpenBLAS-based wheel builds.
88
+ - Fixed several maturin and PyO3 packaging workflow issues for the Python release path.
89
+
90
+ ## [0.1.0] - 2026-03-08
91
+
92
+ ### Added
93
+
94
+ - **Core LMM** - `lmer()` with REML and ML estimation, matching R's `lme4` to 4 decimal places
95
+ - **GLMM** - `glmer()` with Poisson, Binomial, Gaussian, and Gamma families via Laplace approximation
96
+ - **Predictions** - `predict()`, `predict_conditional()`, `predict_response()`, `predict_conditional_response()` with `allow_new_levels` support
97
+ - **Inference** - Satterthwaite and Kenward-Roger degrees of freedom, robust sandwich standard errors
98
+ - **Model comparison** - `anova()` for likelihood ratio tests between nested models
99
+ - **Diagnostics** - `confint()` for Wald confidence intervals, `simulate()` for parametric bootstrap
100
+ - **Formula parser** - Wilkinson notation with nested (`a/b`), crossed (`a + b`), and correlated slope+intercept (`x | group`) random effects
101
+ - **Sparse matrix architecture** - Compressed sparse column format with sparse Cholesky for large datasets
102
+ - **Python bindings** - `lme_python` package via PyO3/maturin with `lmer()`, `glmer()`, `predict()`
103
+ - **Documentation** - `GUIDE.md`, `PYTHON_GUIDE.md`, and cross-language `COMPARISONS.md`