lme-python 0.2.2__tar.gz → 0.2.3__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 (385) hide show
  1. lme_python-0.2.3/.github/FUNDING.yml +1 -0
  2. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/audit.yml +2 -2
  3. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/benchmarks.yml +76 -0
  4. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/cache-prime.yml +3 -3
  5. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/ci.yml +9 -7
  6. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/fuzz-smoke.yml +1 -1
  7. lme_python-0.2.3/.github/workflows/pages.yml +56 -0
  8. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/python-release.yml +1 -1
  9. {lme_python-0.2.2 → lme_python-0.2.3}/AGENTS.md +6 -5
  10. {lme_python-0.2.2 → lme_python-0.2.3}/BENCHMARKS.md +73 -29
  11. {lme_python-0.2.2 → lme_python-0.2.3}/BENCHMARK_COVERAGE.md +17 -9
  12. {lme_python-0.2.2 → lme_python-0.2.3}/CHANGELOG.md +53 -1
  13. {lme_python-0.2.2 → lme_python-0.2.3}/CI_PERFORMANCE.md +7 -1
  14. lme_python-0.2.3/CONTRIBUTING.md +226 -0
  15. {lme_python-0.2.2 → lme_python-0.2.3}/Cargo.lock +1 -1
  16. {lme_python-0.2.2 → lme_python-0.2.3}/Cargo.toml +1 -1
  17. {lme_python-0.2.2 → lme_python-0.2.3}/GUIDE.md +100 -41
  18. {lme_python-0.2.2 → lme_python-0.2.3}/OPTIMIZATION.md +88 -4
  19. lme_python-0.2.3/PKG-INFO +127 -0
  20. lme_python-0.2.3/README.md +220 -0
  21. lme_python-0.2.3/RELEASING.md +157 -0
  22. lme_python-0.2.3/REPO_COMPLETION_BY_AREA.md +132 -0
  23. {lme_python-0.2.2 → lme_python-0.2.3}/Taskfile.yml +10 -0
  24. lme_python-0.2.3/USABILITY.md +110 -0
  25. lme_python-0.2.3/benchmarks/fair-rust-julia-reference-2026-09-09-refactor.json +1693 -0
  26. lme_python-0.2.3/benchmarks/refactor-2026-09-09.json +1854 -0
  27. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/COMPARISONS.md +25 -4
  28. lme_python-0.2.3/completion_manifest.json +159 -0
  29. lme_python-0.2.3/docs/CALO_CALIBRATION.md +98 -0
  30. lme_python-0.2.3/docs/EXAMPLES.md +86 -0
  31. lme_python-0.2.3/docs/README.md +69 -0
  32. lme_python-0.2.3/docs/TROUBLESHOOTING.md +91 -0
  33. lme_python-0.2.3/docs/benchmarks/app.js +397 -0
  34. lme_python-0.2.3/docs/benchmarks/data/latest.json +834 -0
  35. lme_python-0.2.3/docs/benchmarks/index.html +133 -0
  36. {lme_python-0.2.2 → lme_python-0.2.3}/docs/benchmarks/styles.css +62 -3
  37. {lme_python-0.2.2 → lme_python-0.2.3}/docs/index.html +1 -1
  38. {lme_python-0.2.2 → lme_python-0.2.3}/lefthook.yml +4 -0
  39. lme_python-0.2.3/lme-rs-icon.png +0 -0
  40. lme_python-0.2.3/lme-rs.png +0 -0
  41. {lme_python-0.2.2 → lme_python-0.2.3}/python/Cargo.lock +2 -2
  42. {lme_python-0.2.2 → lme_python-0.2.3}/python/Cargo.toml +1 -1
  43. {lme_python-0.2.2 → lme_python-0.2.3}/python/PYTHON_GUIDE.md +163 -112
  44. lme_python-0.2.3/python/README.md +104 -0
  45. lme_python-0.2.3/python/examples/plotting_demo/README.md +101 -0
  46. lme_python-0.2.3/python/examples/verification_project/README.md +51 -0
  47. {lme_python-0.2.2 → lme_python-0.2.3}/python/lme_python.pyi +48 -10
  48. lme_python-0.2.3/python/src/controls.rs +61 -0
  49. lme_python-0.2.3/python/src/fitting.rs +597 -0
  50. lme_python-0.2.3/python/src/input.rs +107 -0
  51. {lme_python-0.2.2 → lme_python-0.2.3}/python/src/lib.rs +131 -727
  52. lme_python-0.2.3/python/tests/test_model_contracts.py +133 -0
  53. {lme_python-0.2.2 → lme_python-0.2.3}/python/uv.lock +4 -4
  54. lme_python-0.2.3/scripts/build_benchmark_site.py +508 -0
  55. lme_python-0.2.3/scripts/ci/README.md +55 -0
  56. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/check_completion_score.py +36 -3
  57. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/lme_ci.py +76 -1
  58. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/explorations/README.md +5 -1
  59. {lme_python-0.2.2 → lme_python-0.2.3}/src/bootstrap.rs +107 -102
  60. {lme_python-0.2.2 → lme_python-0.2.3}/src/contrast.rs +38 -24
  61. {lme_python-0.2.2 → lme_python-0.2.3}/src/cv.rs +31 -85
  62. lme_python-0.2.3/src/display.rs +283 -0
  63. {lme_python-0.2.2 → lme_python-0.2.3}/src/emmeans.rs +3 -3
  64. lme_python-0.2.3/src/execution.rs +48 -0
  65. {lme_python-0.2.2 → lme_python-0.2.3}/src/formula.rs +2 -1
  66. {lme_python-0.2.2 → lme_python-0.2.3}/src/glmm_math.rs +149 -74
  67. {lme_python-0.2.2 → lme_python-0.2.3}/src/intercept_blocked.rs +19 -0
  68. lme_python-0.2.3/src/kenward_roger.rs +81 -0
  69. {lme_python-0.2.2 → lme_python-0.2.3}/src/kr_modcomp.rs +24 -45
  70. {lme_python-0.2.2 → lme_python-0.2.3}/src/kr_vcov_adj.rs +7 -12
  71. {lme_python-0.2.2 → lme_python-0.2.3}/src/lib.rs +218 -560
  72. {lme_python-0.2.2 → lme_python-0.2.3}/src/math.rs +99 -84
  73. {lme_python-0.2.2 → lme_python-0.2.3}/src/mcp.rs +2 -2
  74. lme_python-0.2.3/src/model.rs +240 -0
  75. {lme_python-0.2.2 → lme_python-0.2.3}/src/model_matrix.rs +62 -60
  76. lme_python-0.2.3/src/nlmm/block_solve.rs +132 -0
  77. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/fit.rs +263 -140
  78. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/mod.rs +1 -0
  79. lme_python-0.2.3/src/ols.rs +33 -0
  80. {lme_python-0.2.2 → lme_python-0.2.3}/src/optimizer.rs +138 -101
  81. lme_python-0.2.3/src/predict.rs +179 -0
  82. lme_python-0.2.3/src/prepared.rs +76 -0
  83. {lme_python-0.2.2 → lme_python-0.2.3}/src/profile_ci.rs +6 -11
  84. {lme_python-0.2.2 → lme_python-0.2.3}/src/robust.rs +16 -1
  85. {lme_python-0.2.2 → lme_python-0.2.3}/src/satterthwaite.rs +2 -9
  86. {lme_python-0.2.2 → lme_python-0.2.3}/src/simulate.rs +18 -17
  87. {lme_python-0.2.2 → lme_python-0.2.3}/tests/ci_consolidated.rs +9 -0
  88. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_anova.rs +1 -0
  89. lme_python-0.2.3/tests/test_bug_hunt.rs +353 -0
  90. lme_python-0.2.3/tests/test_bug_hunt_more.rs +212 -0
  91. lme_python-0.2.3/tests/test_bug_hunt_numerics.rs +287 -0
  92. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_coverage_gaps.rs +2 -1
  93. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_failure_modes.rs +14 -14
  94. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_formula_stress.rs +1 -0
  95. lme_python-0.2.3/tests/test_model_contracts.rs +333 -0
  96. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_predict.rs +4 -3
  97. lme_python-0.2.2/CONTRIBUTING.md +0 -220
  98. lme_python-0.2.2/PKG-INFO +0 -67
  99. lme_python-0.2.2/README.md +0 -113
  100. lme_python-0.2.2/RELEASING.md +0 -273
  101. lme_python-0.2.2/REPO_COMPLETION_BY_AREA.md +0 -110
  102. lme_python-0.2.2/USABILITY.md +0 -185
  103. lme_python-0.2.2/completion_manifest.json +0 -20
  104. lme_python-0.2.2/docs/CALO_CALIBRATION.md +0 -124
  105. lme_python-0.2.2/docs/benchmarks/app.js +0 -207
  106. lme_python-0.2.2/docs/benchmarks/index.html +0 -92
  107. lme_python-0.2.2/python/README.md +0 -44
  108. lme_python-0.2.2/python/examples/plotting_demo/README.md +0 -129
  109. lme_python-0.2.2/python/examples/verification_project/README.md +0 -42
  110. lme_python-0.2.2/scripts/build_benchmark_site.py +0 -210
  111. lme_python-0.2.2/scripts/ci/README.md +0 -31
  112. lme_python-0.2.2/src/kenward_roger.rs +0 -357
  113. {lme_python-0.2.2 → lme_python-0.2.3}/.JuliaFormatter.toml +0 -0
  114. {lme_python-0.2.2 → lme_python-0.2.3}/.github/dependabot.yml +0 -0
  115. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/crate-publish-dry-run.yml +0 -0
  116. {lme_python-0.2.2 → lme_python-0.2.3}/.github/workflows/repo-metadata.yml +0 -0
  117. {lme_python-0.2.2 → lme_python-0.2.3}/.gitignore +0 -0
  118. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSE +0 -0
  119. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSES/Apache-2.0.txt +0 -0
  120. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSES/BSD-3-Clause-OpenBLAS.txt +0 -0
  121. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSES/GPL-2.0-or-later.txt +0 -0
  122. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSES/Intel-Simplified-Software-License.txt +0 -0
  123. {lme_python-0.2.2 → lme_python-0.2.3}/LICENSES/LGPL-2.1-only.txt +0 -0
  124. {lme_python-0.2.2 → lme_python-0.2.3}/RELINKING.md +0 -0
  125. {lme_python-0.2.2 → lme_python-0.2.3}/THIRD_PARTY_NOTICES.md +0 -0
  126. {lme_python-0.2.2 → lme_python-0.2.3}/benches/bench_load_production.rs +0 -0
  127. {lme_python-0.2.2 → lme_python-0.2.3}/benches/bench_math.rs +0 -0
  128. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/external-nlmm-inference-python-timings-2026-08-14.json +0 -0
  129. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-04.json +0 -0
  130. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-06.json +0 -0
  131. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-08.json +0 -0
  132. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-direct-intercept-gram.json +0 -0
  133. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-full-lmm.json +0 -0
  134. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-glmm.json +0 -0
  135. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-intercept-setup.json +0 -0
  136. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes-linear-cache.json +0 -0
  137. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes.json +0 -0
  138. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-sleepstudy-slopes.json +0 -0
  139. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09.json +0 -0
  140. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-10-cold-setup-followup.json +0 -0
  141. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-16-cold-fit-lt1.json +0 -0
  142. {lme_python-0.2.2 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json +0 -0
  143. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/bench_external_timings.R +0 -0
  144. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/bench_fair_julia_perf.jl +0 -0
  145. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/bench_fair_julia_timing.jl +0 -0
  146. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/bench_fair_rust_julia.rs +0 -0
  147. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/bench_perf_breakdown.rs +0 -0
  148. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/categorical_anova.R +0 -0
  149. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/categorical_anova.jl +0 -0
  150. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/categorical_anova.py +0 -0
  151. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/categorical_anova.rs +0 -0
  152. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/comparison_chart.png +0 -0
  153. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_cbpp.R +0 -0
  154. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_cbpp.jl +0 -0
  155. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_cbpp.py +0 -0
  156. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_cbpp.rs +0 -0
  157. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_gaussian.R +0 -0
  158. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_gaussian.jl +0 -0
  159. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_gaussian.py +0 -0
  160. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_gaussian.rs +0 -0
  161. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_grouseticks.R +0 -0
  162. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_grouseticks.jl +0 -0
  163. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_grouseticks.py +0 -0
  164. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/glmm_grouseticks.rs +0 -0
  165. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmer_weighted.R +0 -0
  166. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmer_weighted.jl +0 -0
  167. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmer_weighted.py +0 -0
  168. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmer_weighted.rs +0 -0
  169. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_dyestuff.R +0 -0
  170. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_dyestuff.jl +0 -0
  171. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_dyestuff.py +0 -0
  172. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_dyestuff.rs +0 -0
  173. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_pastes.R +0 -0
  174. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_pastes.jl +0 -0
  175. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_pastes.py +0 -0
  176. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_pastes.rs +0 -0
  177. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_penicillin.R +0 -0
  178. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_penicillin.jl +0 -0
  179. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_penicillin.py +0 -0
  180. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/lmm_penicillin.rs +0 -0
  181. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/nlmm_sspower.R +0 -0
  182. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_offset.R +0 -0
  183. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_offset.jl +0 -0
  184. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_probit.R +0 -0
  185. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_probit.jl +0 -0
  186. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_weighted.R +0 -0
  187. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/glmm_weighted.jl +0 -0
  188. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/parity_r_setup.R +0 -0
  189. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/sleepstudy_offset.R +0 -0
  190. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity/sleepstudy_offset.jl +0 -0
  191. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/parity_export.rs +0 -0
  192. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/pastes_emmeans.R +0 -0
  193. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/pastes_glht.R +0 -0
  194. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/r_setup.R +0 -0
  195. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy.R +0 -0
  196. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy.jl +0 -0
  197. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy.py +0 -0
  198. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy.rs +0 -0
  199. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy_confint_profile.R +0 -0
  200. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy_ml.R +0 -0
  201. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy_ml.jl +0 -0
  202. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy_ml.py +0 -0
  203. {lme_python-0.2.2 → lme_python-0.2.3}/comparisons/sleepstudy_ml.rs +0 -0
  204. {lme_python-0.2.2 → lme_python-0.2.3}/examples/batch_sspower_cpu.rs +0 -0
  205. {lme_python-0.2.2 → lme_python-0.2.3}/examples/bench_external_timings.rs +0 -0
  206. {lme_python-0.2.2 → lme_python-0.2.3}/examples/dump_cascade_fixtures.rs +0 -0
  207. {lme_python-0.2.2 → lme_python-0.2.3}/examples/explore_formula_ast.rs +0 -0
  208. {lme_python-0.2.2 → lme_python-0.2.3}/examples/explore_mcp_adjust.rs +0 -0
  209. {lme_python-0.2.2 → lme_python-0.2.3}/examples/explore_theta_grid.rs +0 -0
  210. {lme_python-0.2.2 → lme_python-0.2.3}/examples/nlmm_fixture_dump.rs +0 -0
  211. {lme_python-0.2.2 → lme_python-0.2.3}/legal/dependency-license-exceptions.json +0 -0
  212. {lme_python-0.2.2 → lme_python-0.2.3}/legal/fixture-provenance.json +0 -0
  213. {lme_python-0.2.2 → lme_python-0.2.3}/mise.toml +0 -0
  214. {lme_python-0.2.2 → lme_python-0.2.3}/pyproject.toml +0 -0
  215. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/glmer_cbpp.py +0 -0
  216. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/glmer_grouseticks.py +0 -0
  217. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/lm_basics.py +0 -0
  218. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/lmer_sleepstudy.py +0 -0
  219. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/model_comparison.py +0 -0
  220. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/.gitignore +0 -0
  221. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/compare_plots.py +0 -0
  222. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/figure_specs.py +0 -0
  223. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/find_rscript.py +0 -0
  224. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/numeric_overlay.py +0 -0
  225. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/paths.py +0 -0
  226. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/plot_demo.py +0 -0
  227. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/plot_r.R +0 -0
  228. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/plotting_demo/run_all.py +0 -0
  229. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/verification_project/conftest.py +0 -0
  230. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/verification_project/parity.py +0 -0
  231. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/verification_project/paths.py +0 -0
  232. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/verification_project/run.py +0 -0
  233. {lme_python-0.2.2 → lme_python-0.2.3}/python/examples/verification_project/test_parity.py +0 -0
  234. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_api_parity.py +0 -0
  235. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_basic.py +0 -0
  236. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_cv.py +0 -0
  237. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_dataframe_inputs.py +0 -0
  238. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_golden_parity.py +0 -0
  239. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_nlmer_custom_mean.py +0 -0
  240. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_simulate_scale.py +0 -0
  241. {lme_python-0.2.2 → lme_python-0.2.3}/python/tests/test_wheel_notices.py +0 -0
  242. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/check_legal_compliance.py +0 -0
  243. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/julia_format.jl +0 -0
  244. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/package_wheel_notices.py +0 -0
  245. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/r_format.R +0 -0
  246. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/ci/run.sh +0 -0
  247. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/dump_dyestuff.R +0 -0
  248. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/dump_pastes.R +0 -0
  249. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/local_ci.ps1 +0 -0
  250. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/local_ci.sh +0 -0
  251. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/make_penicillin_csv.py +0 -0
  252. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/plot_comparisons.py +0 -0
  253. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/run_cross_language_benchmarks.py +0 -0
  254. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/run_external_timings.py +0 -0
  255. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/run_fair_rust_julia_benchmark.py +0 -0
  256. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/run_perf_breakdown.py +0 -0
  257. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/sync_github_repo_metadata.py +0 -0
  258. {lme_python-0.2.2 → lme_python-0.2.3}/scripts/verify_cross_language_parity.py +0 -0
  259. {lme_python-0.2.2 → lme_python-0.2.3}/src/anova.rs +0 -0
  260. {lme_python-0.2.2 → lme_python-0.2.3}/src/anova_contrasts.rs +0 -0
  261. {lme_python-0.2.2 → lme_python-0.2.3}/src/basis.rs +0 -0
  262. {lme_python-0.2.2 → lme_python-0.2.3}/src/ddf.rs +0 -0
  263. {lme_python-0.2.2 → lme_python-0.2.3}/src/family.rs +0 -0
  264. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/formula.rs +0 -0
  265. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/mean.rs +0 -0
  266. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/mean_fn.rs +0 -0
  267. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/predict.rs +0 -0
  268. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/re_cov.rs +0 -0
  269. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/self_start.rs +0 -0
  270. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssasymp.rs +0 -0
  271. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssasympoff.rs +0 -0
  272. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssasymporig.rs +0 -0
  273. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssbiexp.rs +0 -0
  274. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssfpl.rs +0 -0
  275. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssgompertz.rs +0 -0
  276. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/sslogis.rs +0 -0
  277. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssmicmen.rs +0 -0
  278. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/sspower.rs +0 -0
  279. {lme_python-0.2.2 → lme_python-0.2.3}/src/nlmm/ssweibull.rs +0 -0
  280. {lme_python-0.2.2 → lme_python-0.2.3}/src/perf_diag.rs +0 -0
  281. {lme_python-0.2.2 → lme_python-0.2.3}/src/quadrature.rs +0 -0
  282. {lme_python-0.2.2 → lme_python-0.2.3}/src/studentized_range.rs +0 -0
  283. {lme_python-0.2.2 → lme_python-0.2.3}/tests/categorical_anova_test.rs +0 -0
  284. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/cbpp_binary.csv +0 -0
  285. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/cbpp_binary_weighted.csv +0 -0
  286. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/dyestuff.csv +0 -0
  287. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/dyestuff_intercept_reml.json +0 -0
  288. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_binomial.json +0 -0
  289. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_binomial_agq.json +0 -0
  290. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_binomial_cloglog.json +0 -0
  291. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_binomial_probit.json +0 -0
  292. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_binomial_weighted.json +0 -0
  293. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_gamma_dyestuff.json +0 -0
  294. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_poisson.json +0 -0
  295. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/glmm_poisson_offset.json +0 -0
  296. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/golden_parity_manifest.json +0 -0
  297. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/grouseticks.csv +0 -0
  298. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/intercept_only.json +0 -0
  299. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/mock_crossed.json +0 -0
  300. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/mock_ml.json +0 -0
  301. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/orange.csv +0 -0
  302. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/orange_nlmer.json +0 -0
  303. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/pastes.csv +0 -0
  304. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/pastes_cask_reml.json +0 -0
  305. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/pastes_emmeans.json +0 -0
  306. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/pastes_glht_tukey.json +0 -0
  307. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/penicillin.csv +0 -0
  308. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/penicillin.json +0 -0
  309. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/r_edge_case_matrix.json +0 -0
  310. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/random_slopes.json +0 -0
  311. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sleepstudy.csv +0 -0
  312. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sleepstudy_confint_profile.json +0 -0
  313. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sleepstudy_confint_profile_vc.json +0 -0
  314. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sleepstudy_offset_reml.json +0 -0
  315. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssasymp_synthetic.csv +0 -0
  316. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssasympoff_nlmer.json +0 -0
  317. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssasympoff_synthetic.csv +0 -0
  318. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssasymporig_nlmer.json +0 -0
  319. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssasymporig_synthetic.csv +0 -0
  320. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssbiexp_nlmer.json +0 -0
  321. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssbiexp_synthetic.csv +0 -0
  322. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssfol_nlmer.json +0 -0
  323. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssfpl_nlmer.json +0 -0
  324. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssfpl_synthetic.csv +0 -0
  325. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssgompertz_nlmer.json +0 -0
  326. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssgompertz_synthetic.csv +0 -0
  327. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssmicmen_nlmer.json +0 -0
  328. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssmicmen_synthetic.csv +0 -0
  329. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sspower_nlmer.json +0 -0
  330. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/sspower_synthetic.csv +0 -0
  331. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssweibull_nlmer.json +0 -0
  332. {lme_python-0.2.2 → lme_python-0.2.3}/tests/data/ssweibull_synthetic.csv +0 -0
  333. {lme_python-0.2.2 → lme_python-0.2.3}/tests/gen_nlmm_extras.R +0 -0
  334. {lme_python-0.2.2 → lme_python-0.2.3}/tests/generate_mock_data.py +0 -0
  335. {lme_python-0.2.2 → lme_python-0.2.3}/tests/generate_nlmm_fixtures.R +0 -0
  336. {lme_python-0.2.2 → lme_python-0.2.3}/tests/generate_r_edge_case_matrix.R +0 -0
  337. {lme_python-0.2.2 → lme_python-0.2.3}/tests/generate_test_data.R +0 -0
  338. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_anova_types.rs +0 -0
  339. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_bootstrap.rs +0 -0
  340. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_conditional_real.rs +0 -0
  341. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_confint_profile.rs +0 -0
  342. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_confint_simulate.rs +0 -0
  343. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_contrast.rs +0 -0
  344. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_coverage_edge_cases.rs +0 -0
  345. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_crossed_mock.rs +0 -0
  346. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_cv.rs +0 -0
  347. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_e2e_lmer.rs +0 -0
  348. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_edge_cases.rs +0 -0
  349. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_emmeans.rs +0 -0
  350. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_errors.rs +0 -0
  351. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_explorations.rs +0 -0
  352. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_external_timings_artifact.rs +0 -0
  353. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_formula.rs +0 -0
  354. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_gaps.rs +0 -0
  355. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_glmm.rs +0 -0
  356. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_glmm_links.rs +0 -0
  357. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_glmm_offset_grouseticks.rs +0 -0
  358. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_glmm_weighted.rs +0 -0
  359. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_golden_parity.rs +0 -0
  360. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_intercept_only.rs +0 -0
  361. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_kenward_roger.rs +0 -0
  362. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_kr_modcomp_pastes.rs +0 -0
  363. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_mcp.rs +0 -0
  364. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_ml_optimization.rs +0 -0
  365. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_agq.rs +0 -0
  366. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_bounds.rs +0 -0
  367. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_custom_mean.rs +0 -0
  368. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_orange.rs +0 -0
  369. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_orange_multi_re.rs +0 -0
  370. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_self_start.rs +0 -0
  371. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_ss_new_means.rs +0 -0
  372. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_ssasymp.rs +0 -0
  373. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_ssasymp_off_orig.rs +0 -0
  374. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_ssfol.rs +0 -0
  375. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_ssmicmen.rs +0 -0
  376. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_nlmm_sspower.rs +0 -0
  377. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_no_intercept.rs +0 -0
  378. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_numerical_parity.rs +0 -0
  379. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_production_load.rs +0 -0
  380. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_r_edge_case_matrix.rs +0 -0
  381. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_random_slopes.rs +0 -0
  382. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_robust.rs +0 -0
  383. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_sandwich_math.R +0 -0
  384. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_satterthwaite.rs +0 -0
  385. {lme_python-0.2.2 → lme_python-0.2.3}/tests/test_statistical_identities.rs +0 -0
@@ -0,0 +1 @@
1
+ github: x4g4p3x
@@ -28,7 +28,7 @@ jobs:
28
28
  toolchain: stable
29
29
 
30
30
  - name: Install cargo-audit
31
- uses: taiki-e/install-action@7f4eb899022d8fe70b20c4f3de697aa85c309026 # v2
31
+ uses: taiki-e/install-action@84f5ac3124727fb3d284d4d22ee9ab3654fd09a6 # v2
32
32
  with:
33
33
  tool: cargo-audit@0.22.1
34
34
 
@@ -48,7 +48,7 @@ jobs:
48
48
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
49
49
 
50
50
  - name: Set up uv
51
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
51
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
52
52
  with:
53
53
  python-version: "3.11"
54
54
  enable-cache: true
@@ -18,6 +18,7 @@ on:
18
18
  permissions:
19
19
  contents: write
20
20
  id-token: write
21
+ pages: write
21
22
 
22
23
  env:
23
24
  CARGO_TERM_COLOR: always
@@ -200,3 +201,78 @@ jobs:
200
201
  echo "Cross-language benchmark script failed with exit code ${{ steps.cross_language_run.outputs.status }}." >&2
201
202
  exit 1
202
203
 
204
+ pages:
205
+ name: Publish Benchmark Dashboard
206
+ runs-on: ubuntu-latest
207
+ needs:
208
+ - criterion
209
+ - cross-language
210
+ if: >
211
+ always() &&
212
+ needs.criterion.result == 'success' &&
213
+ needs.cross-language.result == 'success' &&
214
+ (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/'))
215
+ permissions:
216
+ contents: read
217
+ pages: write
218
+ id-token: write
219
+ environment:
220
+ name: github-pages
221
+ url: ${{ steps.deploy.outputs.page_url }}
222
+ concurrency:
223
+ group: pages
224
+ cancel-in-progress: true
225
+ steps:
226
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
227
+
228
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
229
+ with:
230
+ python-version: "3.11"
231
+
232
+ - name: Download cross-language artifact
233
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
234
+ with:
235
+ name: cross-language-${{ github.sha }}
236
+ path: site-input
237
+
238
+ - name: Resolve release URL
239
+ id: release_url
240
+ run: |
241
+ if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
242
+ echo "value=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/releases/tag/${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
243
+ else
244
+ echo "value=" >> "$GITHUB_OUTPUT"
245
+ fi
246
+
247
+ - name: Build benchmark dashboard
248
+ run: |
249
+ set -euo pipefail
250
+ mkdir -p site
251
+ cp -R docs/. site/
252
+ extra=()
253
+ fair_json="$(find site-input -name 'fair-rust-julia-*.json' -print -quit || true)"
254
+ cross_json="$(find site-input -name 'cross-language-*.json' -print -quit || true)"
255
+ if [ -n "${fair_json}" ]; then
256
+ extra+=(--ci-fair-json "${fair_json}" --fair-asset-name "fair-rust-julia-${GITHUB_SHA}.json")
257
+ fi
258
+ if [ -n "${cross_json}" ]; then
259
+ extra+=(--cross-language-json "${cross_json}" --cross-language-asset-name "cross-language-${GITHUB_SHA}.json")
260
+ fi
261
+ python3 scripts/build_benchmark_site.py \
262
+ --output-dir site/benchmarks/data \
263
+ --run-url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \
264
+ --release-url "${{ steps.release_url.outputs.value }}" \
265
+ --criterion-asset-name "criterion-${GITHUB_SHA}.tar.gz" \
266
+ --ref-name "${GITHUB_REF_NAME}" \
267
+ "${extra[@]+"${extra[@]}"}"
268
+
269
+ - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
270
+
271
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
272
+ with:
273
+ path: site
274
+
275
+ - name: Deploy to GitHub Pages
276
+ id: deploy
277
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
278
+
@@ -54,7 +54,7 @@ jobs:
54
54
  if: matrix.os == 'macos-latest'
55
55
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
56
56
  - name: Cache Rust dependencies
57
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
57
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
58
58
  with:
59
59
  shared-key: ci-v2-${{ runner.os }}-rust-test
60
60
  cache-workspace-crates: ${{ runner.os != 'Linux' }}
@@ -76,7 +76,7 @@ jobs:
76
76
  - name: Install OpenBLAS
77
77
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
78
78
  - name: Cache production-load Rust dependencies
79
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
79
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
80
80
  with:
81
81
  shared-key: ci-v2-${{ runner.os }}-rust-release
82
82
  cache-workspace-crates: true
@@ -105,7 +105,7 @@ jobs:
105
105
  if: matrix.os == 'macos-latest'
106
106
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
107
107
  - name: Cache Python Rust dependencies
108
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
108
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
109
109
  with:
110
110
  shared-key: ci-${{ runner.os }}-python
111
111
  workspaces: python -> target
@@ -36,6 +36,8 @@ jobs:
36
36
  run: python3 scripts/ci/lme_ci.py legal
37
37
  - name: Validate Markdown links
38
38
  run: python3 scripts/ci/lme_ci.py docs-links
39
+ - name: Validate benchmark dashboard data
40
+ run: python3 scripts/ci/lme_ci.py benchmark-site --check
39
41
  - name: Validate completion score claims
40
42
  run: python3 scripts/ci/lme_ci.py completion-check
41
43
 
@@ -56,7 +58,7 @@ jobs:
56
58
  - name: Install OpenBLAS
57
59
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
58
60
  - name: Cache Rust dependencies
59
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
61
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
60
62
  with:
61
63
  shared-key: ci-v2-${{ runner.os }}-rust-test
62
64
  save-if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/master' }}
@@ -113,7 +115,7 @@ jobs:
113
115
  toolchain: stable
114
116
 
115
117
  - name: Cache Rust dependencies
116
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
118
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
117
119
  with:
118
120
  shared-key: ci-v2-${{ runner.os }}-rust-test
119
121
  cache-workspace-crates: true
@@ -141,7 +143,7 @@ jobs:
141
143
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
142
144
 
143
145
  - name: Restore Rust dependencies
144
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
146
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
145
147
  with:
146
148
  shared-key: ci-v2-${{ runner.os }}-rust-release
147
149
  cache-workspace-crates: true
@@ -169,13 +171,13 @@ jobs:
169
171
  - name: Install OpenBLAS
170
172
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
171
173
  - name: Cache Python Rust dependencies
172
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
174
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
173
175
  with:
174
176
  shared-key: ci-${{ runner.os }}-python
175
177
  workspaces: python -> target
176
178
  save-if: ${{ matrix.python-version == '3.11' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/master') }}
177
179
  - name: Set up uv
178
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
180
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
179
181
  with:
180
182
  python-version: ${{ matrix.python-version }}
181
183
  enable-cache: true
@@ -205,13 +207,13 @@ jobs:
205
207
  if: matrix.os == 'macos-latest'
206
208
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
207
209
  - name: Cache Python Rust dependencies
208
- uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
210
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
209
211
  with:
210
212
  shared-key: ci-${{ runner.os }}-python
211
213
  workspaces: python -> target
212
214
  save-if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/master' }}
213
215
  - name: Set up uv
214
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
216
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
215
217
  with:
216
218
  python-version: "3.11"
217
219
  enable-cache: true
@@ -31,7 +31,7 @@ jobs:
31
31
  - name: Install cargo-fuzz
32
32
  run: cargo install cargo-fuzz@0.13.2 --locked
33
33
 
34
- - uses: Swatinem/rust-cache@a45951ff880207c249adf57334cf2e9bd81d6e1e # v2
34
+ - uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
35
35
  with:
36
36
  workspaces: fuzz
37
37
 
@@ -0,0 +1,56 @@
1
+ name: GitHub Pages
2
+
3
+ # Publishes the static dashboard from checked-in docs/ plus regenerated JSON.
4
+ # Tag/manual benchmark runs can overlay CI artifacts via benchmarks.yml.
5
+
6
+ on:
7
+ push:
8
+ branches:
9
+ - master
10
+ paths:
11
+ - "docs/**"
12
+ - "benchmarks/*.json"
13
+ - "scripts/build_benchmark_site.py"
14
+ - "scripts/ci/lme_ci.py"
15
+ - ".github/workflows/pages.yml"
16
+ workflow_dispatch:
17
+
18
+ permissions:
19
+ contents: read
20
+ pages: write
21
+ id-token: write
22
+
23
+ concurrency:
24
+ group: pages
25
+ cancel-in-progress: true
26
+
27
+ jobs:
28
+ build:
29
+ name: Build dashboard
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
+
34
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
35
+ with:
36
+ python-version: "3.11"
37
+
38
+ - name: Assemble site
39
+ run: python3 scripts/ci/lme_ci.py benchmark-site --site-dir site
40
+
41
+ - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
42
+
43
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
44
+ with:
45
+ path: site
46
+
47
+ deploy:
48
+ name: Deploy
49
+ needs: build
50
+ runs-on: ubuntu-latest
51
+ environment:
52
+ name: github-pages
53
+ url: ${{ steps.deploy.outputs.page_url }}
54
+ steps:
55
+ - id: deploy
56
+ uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1
@@ -256,7 +256,7 @@ jobs:
256
256
  uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
257
257
 
258
258
  - name: Upload to GitHub Release
259
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
259
+ uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
260
260
  with:
261
261
  files: dist/*
262
262
  tag_name: ${{ inputs.release_tag }}
@@ -10,7 +10,7 @@ Use the smallest validation tier that proves the change is sound, then run the e
10
10
  | Python bindings | `task lint:python`, `task python` |
11
11
  | CI, manifests, release tooling | `task preflight`; use `task ci` before a release or broad refactor |
12
12
  | R / Julia comparison scripts | `task lint:comparisons`; use `task lint:comparisons:required` when the formatters are installed |
13
- | Documentation or portable examples | `task docs:check`; use `task consumer:smoke` when install or example behavior changes |
13
+ | Documentation or portable examples | `task docs:check` (includes dashboard JSON drift); use `task consumer:smoke` when install or example behavior changes |
14
14
  | LMM throughput paths (`src/math.rs`, `src/optimizer.rs`) | Read [OPTIMIZATION.md](OPTIMIZATION.md) and run the applicable fair-harness cases |
15
15
  | Completion score files: `README.md`, `REPO_COMPLETION_BY_AREA.md`, `completion_manifest.json`, or `scripts/ci/check_completion_score.py` | `task completion:check` |
16
16
 
@@ -20,10 +20,10 @@ For a change that crosses several rows, run every applicable check. Full Rust in
20
20
 
21
21
  The completion headline in [README.md](README.md) and every percentage in [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md) are generated claims, not values to edit by hand.
22
22
 
23
- - [completion_manifest.json](completion_manifest.json) is the source of truth: it declares binary scope commitments, their weights, and evidence paths.
24
- - `task completion:check` validates evidence-path existence, score arithmetic, report rows, and the README headline.
25
- - A completion item may be marked complete only when its stated evidence is current. Partial or stale evidence earns zero.
26
- - Do not raise a score merely because an API exists, a focused benchmark passes, or the stated scope has been narrowed. Update the manifest and supporting evidence in the same change.
23
+ - [completion_manifest.json](completion_manifest.json) is the source of truth: it declares binary scope commitments, their weights, locked `scope` strings, evidence paths, and a `gap` for every incomplete criterion.
24
+ - `task completion:check` validates schema version 2 (names, scopes, gaps), evidence-path existence, score arithmetic, report rows, and the README headline.
25
+ - A completion item may be marked complete only when its **locked scope** is met and its stated evidence is current. Partial, stale, or substituted evidence earns zero.
26
+ - Do not raise a score merely because an API exists, a focused benchmark passes, or the stated scope has been narrowed or replaced. Do not edit `scope` to make an existing artifact pass. Update the manifest and supporting evidence in the same change.
27
27
 
28
28
  `task ci` runs this check automatically. Run it directly whenever a completion-related file changes.
29
29
 
@@ -95,6 +95,7 @@ For repository-metadata token issues, set `REPO_ADMIN_TOKEN` locally and run `ta
95
95
  | `task audit` / `task legal` | Security audit / provenance and license checks |
96
96
  | `task completion:check` | Verify the manifest-derived completion score and published markers |
97
97
  | `task docs:check` / `task consumer:smoke` | Validate docs/links/API examples / run clean-install Rust and Python workflows |
98
+ | `task benchmarks:site` | Regenerate `docs/benchmarks/data/latest.json` from checked-in reference JSON |
98
99
  | `task benchmarks:fair-rust-julia` | Fair fit-only Rust vs MixedModels.jl timing when Julia packages are installed |
99
100
  | `task benchmarks:perf-breakdown` | Rust phase timings against Julia optimizer evaluation counts |
100
101
  | `task benchmarks:external-timings` | Fair-ish `nlmer`, post-fit inference, and Python FFI timings vs R when available |
@@ -1,8 +1,38 @@
1
- # Benchmarks
1
+ # Benchmark guide and measurement history
2
2
 
3
- This repository includes Criterion benchmarks for the Rust crate. Performance is an important selling point of `lme-rs`, but the benchmark suite should be described accurately: it is useful and non-trivial, yet it is not comprehensive enough to support blanket speed claims on its own.
3
+ [Documentation](docs/README.md) · [Coverage map](BENCHMARK_COVERAGE.md) · [Optimization notes](OPTIMIZATION.md) · [Dashboard](https://x4g4p3x.github.io/lme-rs/benchmarks/)
4
4
 
5
- **External reference map:** [BENCHMARK_COVERAGE.md](BENCHMARK_COVERAGE.md) lists which workflows have tier-A (fair) MixedModels.jl timing vs Rust-only Criterion benches. Use it before raising [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md) axis (3) percentages.
5
+ Choose a harness by the question you want to answer. Numerical parity and
6
+ throughput are separate forms of evidence.
7
+
8
+ ## Choose a benchmark
9
+
10
+ | Question | Command | What is timed |
11
+ |:---------|:--------|:--------------|
12
+ | Did a Rust operation regress? | `cargo bench --locked --bench bench_math` | Criterion operations, fit paths, and inference |
13
+ | How does matched fitting compare with Julia? | `task benchmarks:fair-rust-julia` | Fit-only medians with shared inputs and warmups |
14
+ | Where is fitting time spent? | `task benchmarks:perf-breakdown` | Rust phases and Julia optimizer evaluations |
15
+ | What do R inference/NLMM and Python overhead cost? | `task benchmarks:external-timings` | Operations supported by the external harness |
16
+ | Do cross-language example scripts run? | `python scripts/run_cross_language_benchmarks.py` | Whole scripts, including runtime startup |
17
+ | Does the dashboard match its source JSON? | `task docs:check` | Data drift validation, not a new measurement |
18
+
19
+ Run from the repository root. External harnesses require their reference
20
+ runtimes and packages; a skipped implementation is not a measured comparison.
21
+ See [the coverage map](BENCHMARK_COVERAGE.md#running-benchmarks) for setup.
22
+
23
+ ## Reading order
24
+
25
+ - [Suite coverage](#current-benchmark-coverage) and [remaining gaps](#what-it-does-not-cover-yet)
26
+ - [Methodology](#cross-language-benchmark-methodology)
27
+ - [Commands](#how-to-run-the-benchmarks)
28
+ - [Fair reference results](#fair-rust-vs-julia-reference-results)
29
+ - [Published artifacts](#latest-published-results)
30
+ - [Interpretation](#how-to-interpret-results)
31
+
32
+ The detailed experiments below are **dated measurement history**. A wording
33
+ update does not rerun them. The July 22 reference contains 10 LMM and 2 GLMM
34
+ cases; later GLMM fitting changes mean its GLMM rows cannot establish current
35
+ performance. Hardware, revision, BLAS, and timing boundaries matter.
6
36
 
7
37
  ## Native formula parser optimization (2026-08-10)
8
38
 
@@ -76,19 +106,24 @@ What the repo still does not provide is a fully normalized cross-ecosystem harne
76
106
  It does not yet isolate or compare:
77
107
 
78
108
  - prediction throughput on large crossed and nested structures
79
- - weighted GLMM-style workflows if those are added later
109
+ - weighted GLMM throughput across representative model sizes
80
110
  - GLMM inference and post-fit helper costs beyond the currently benchmarked paths
81
111
  - confidence interval and simulation throughput across multiple model sizes
82
112
 
83
113
  ### Missing benchmark dimensions
84
114
 
85
- It does not currently sweep over controlled workload dimensions such as:
115
+ The Rust suite already includes parameterized observation/group-count and
116
+ nested/crossed complexity sweeps. It does not provide a complete factorial
117
+ comparison across every combination of:
118
+
119
+ - response family and link
120
+ - random-effect dimension and correlation structure
121
+ - dense-like versus sparse-like layouts
122
+ - prediction, simulation, and inference workloads
123
+ - reference language and optimizer configuration
86
124
 
87
- - number of observations
88
- - number of groups
89
- - random intercept vs random slope structure
90
- - family and link combinations
91
- - dense-like vs sparse-like random-effect structure
125
+ Use a named case that represents your workload rather than inferring coverage
126
+ from the existence of one large synthetic benchmark.
92
127
 
93
128
  ## Cross-language benchmark methodology
94
129
 
@@ -230,7 +265,7 @@ Work on [`src/math.rs`](src/math.rs) and [`src/optimizer.rs`](src/optimizer.rs):
230
265
 
231
266
  ### 2026-07-08 blocked augmented Cholesky (MixedModels-style)
232
267
 
233
- Implemented [`src/intercept_blocked.rs`](src/intercept_blocked.rs): precomputed per-RE Gram blocks, RE ordering by level count, and per-θ **blocked Cholesky** in the layout of MixedModels.jl [`updateL!`](https://github.com/JuliaStats/MixedModels.jl/blob/main/src/linearmixedmodel.jl) — profile deviance from the factored `Xy` block **without** q-dimensional LDL solves or explicit β on the hot path. Full write-up: **[OPTIMIZATION.md § Blocked augmented Cholesky](OPTIMIZATION.md#blocked-augmented-cholesky--what-delivered-the-crossed-speedup-2026-07-08)**.
268
+ Implemented [`src/intercept_blocked.rs`](src/intercept_blocked.rs): precomputed per-RE Gram blocks, RE ordering by level count, and per-θ **blocked Cholesky** in the layout of MixedModels.jl [`updateL!`](https://github.com/JuliaStats/MixedModels.jl/blob/main/src/linearmixedmodel.jl) — profile deviance from the factored `Xy` block **without** q-dimensional LDL solves or explicit β on the hot path. Full write-up: **[OPTIMIZATION.md § Blocked augmented Cholesky](OPTIMIZATION.md#blocked-augmented-cholesky)**.
234
269
 
235
270
  **Recorded:** 2026-07-08, same Windows AMD64 workstation; `rustc 1.96.0`; 2 warmups + 10 measured fits (`scripts/run_fair_rust_julia_benchmark.py --implementations rust`).
236
271
 
@@ -246,7 +281,7 @@ Implemented [`src/intercept_blocked.rs`](src/intercept_blocked.rs): precomputed
246
281
 
247
282
  ### 2026-07-08 blocked Cholesky hot-path pass
248
283
 
249
- Removed per-θ **clone/alloc** overhead in [`src/intercept_blocked.rs`](src/intercept_blocked.rs) (in-place Schur, fused `assign_scaled`, reused workspaces) and tightened the ML 2D log-grid to **5×5 + 4×4** (~42 evals). Nested sparse crosses remain on reused LDL (`fits_blocked_gate`). Details: **[OPTIMIZATION.md § Closing the crossed_20k gap](OPTIMIZATION.md#closing-the-crossed_20k-gap-vs-julia-14-ms)**.
284
+ Removed per-θ **clone/alloc** overhead in [`src/intercept_blocked.rs`](src/intercept_blocked.rs) (in-place Schur, fused `assign_scaled`, reused workspaces) and tightened the ML 2D log-grid to **5×5 + 4×4** (~42 evals). Nested sparse crosses remain on reused LDL (`fits_blocked_gate`). Details: **[OPTIMIZATION.md § Closing the crossed_20k gap](OPTIMIZATION.md#progress-timeline)**.
250
285
 
251
286
  **Recorded:** 2026-07-08, same Windows AMD64 workstation; `rustc 1.96.0`; 2 warmups + 10 measured fits (`--implementations rust`).
252
287
 
@@ -262,7 +297,7 @@ Removed per-θ **clone/alloc** overhead in [`src/intercept_blocked.rs`](src/inte
262
297
 
263
298
  ### 2026-07-08 GEMM, batched trisolve, and prepared fit
264
299
 
265
- Further blocked-kernel tuning in [`src/intercept_blocked.rs`](src/intercept_blocked.rs) plus **`prepare_lmer` / `fit_prepared`** in [`src/lib.rs`](src/lib.rs). Full write-up: **[OPTIMIZATION.md § Blocked kernel tuning](OPTIMIZATION.md#blocked-kernel-tuning--gemm-batched-trisolve-prepared-fit-2026-07-08-continued)**.
300
+ Further blocked-kernel tuning in [`src/intercept_blocked.rs`](src/intercept_blocked.rs) plus **`prepare_lmer` / `fit_prepared`** in [`src/lib.rs`](src/lib.rs). Full write-up: **[OPTIMIZATION.md § Blocked kernel tuning](OPTIMIZATION.md#blocked-kernel-tuning)**.
266
301
 
267
302
  **Recorded:** 2026-07-08, same Windows AMD64 workstation; `rustc 1.96.0`; `bench_perf_breakdown` with 1 warmup + measured `fit_prepared`.
268
303
 
@@ -433,9 +468,12 @@ The repository also includes a dedicated workflow in [.github/workflows/benchmar
433
468
  - runs the cross-language example-script benchmark
434
469
  - uploads the resulting artifacts in CI
435
470
  - attaches them to GitHub Releases on tag pushes
471
+ - publishes the fair-harness dashboard to GitHub Pages at [`https://x4g4p3x.github.io/lme-rs/benchmarks/`](https://x4g4p3x.github.io/lme-rs/benchmarks/)
436
472
 
437
473
  ## Latest published results
438
474
 
475
+ The public dashboard at [`https://x4g4p3x.github.io/lme-rs/benchmarks/`](https://x4g4p3x.github.io/lme-rs/benchmarks/) charts the checked-in fair Rust vs MixedModels.jl reference plus the Julia-free external timings. Tag/manual [benchmark workflow](.github/workflows/benchmarks.yml) runs can overlay GitHub-hosted runner JSON. Rebuild the committed payload with `task benchmarks:site` after changing a reference file.
476
+
439
477
  Release-attached Criterion and cross-language JSON artifacts are uploaded by [`.github/workflows/benchmarks.yml`](.github/workflows/benchmarks.yml) on `v*` tags (and manual dispatch). Prefer the [latest GitHub Release](https://github.com/x4g4p3x/lme-rs/releases/latest) for downloadable CI artifacts, and [`CHANGELOG.md`](CHANGELOG.md) for which crate version each tag ships.
440
478
 
441
479
  **Engineering source of truth for fair Rust vs Julia fit timing** is the checked-in reference JSON under [`benchmarks/`](benchmarks/), especially the full strict-target [2026-07-22 tier-A artifact](benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json). Older tags (for example [v0.1.3](https://github.com/x4g4p3x/lme-rs/releases/tag/v0.1.3)) still carry historical CI tarballs useful for release-to-release comparison, but they are **not** the current performance baseline.
@@ -453,15 +491,24 @@ If you want to cite benchmark results in release notes or external docs, prefer
453
491
 
454
492
  ## How to interpret results
455
493
 
456
- Use the existing suite primarily for:
494
+ Compare medians from the same machine, model, and measurement boundary.
457
495
 
458
- - detecting regressions after changes to fitting logic
459
- - tracking the cost of core LMM and GLMM paths over time
460
- - sanity-checking large synthetic scaling cases
496
+ | Metric | Includes | Appropriate use |
497
+ |:-------|:---------|:----------------|
498
+ | `cold_fit` | One fit including its setup | One-shot API latency |
499
+ | `prepare_lmer` | Reusable design construction | Setup cost |
500
+ | `fit_prepared` | Fitting a cached design | Repeated fixed-design work |
501
+ | Whole-script elapsed time | Startup, I/O, setup, fitting, and printed output | End-to-end script cost |
502
+ | Criterion operation | The operation selected by the benchmark | Within-Rust regression tracking |
461
503
 
462
- Do not use the current suite alone as evidence that `lme-rs` is universally faster than `lme4`, `statsmodels`, or `MixedModels.jl`.
504
+ A Rust/Julia ratio below 1 means Rust was faster for that measured row.
505
+ Do not compare a hot prepared fit to a competitor's full process startup.
463
506
 
464
- The [fair Rust vs Julia harness](#fair-rust-vs-julia-reference-results) on the 2026-07-06 Windows reference showed **MixedModels.jl still faster on every fit-only case**, but the gap **narrowed sharply** on random-intercept workloads (~**2×** vs ~**5–9×** on the 2026-07-04 baseline). Crossed (~**19×**) and nested (~**8×**) were the main gaps on that reference. An [2026-07-07 pass](#fair-rust-julia-2026-07-07-wip) cuts crossed to ~**8×** and nested to ~**2.5×**; [blocked Cholesky](#fair-rust-julia-2026-07-08-blocked-cholesky) and [hot-path tuning](#fair-rust-julia-2026-07-08-blocked-hotpath) cut cold `lmer()` to ~**52 ms**; a [GEMM + prepared-fit pass](#fair-rust-julia-2026-07-08-gemm-prepared) brings **`fit_prepared` to ~13 ms** (~**1×** Julia on `crossed_20k`); a [setup/post-fit pass](#fair-rust-julia-2026-07-08-setup-postfit) cuts cold `lmer()` to **~22 ms** (~**1.4×** Julia on crossed, **~1.1×** on random intercept); a [prepare ownership pass](#fair-rust-julia-2026-07-08-prepare-ownership) brings **`random_intercept_10k` ahead of Julia** and crossed to **~1.3×**; a [nested blocked path + post-fit backsolve pass](#fair-rust-julia-2026-07-08-nested-postfit) brings tier-A cases **within ~2× Julia** on cold `lmer()` (`crossed_20k` **~1.2×**, hot **`fit_prepared` beats Julia** on all three). See [OPTIMIZATION.md](OPTIMIZATION.md) for engineering detail. Treat these as versioned datapoints — re-run the harness on your hardware before citing speed claims.
507
+ The historical sections document how earlier crossed and nested bottlenecks
508
+ were reduced. They are not predictions for every model shape. The
509
+ [July 22 artifact](benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json)
510
+ and [coverage map](BENCHMARK_COVERAGE.md) are the concise entry points to that
511
+ reference set. Re-run after relevant code changes before making a new claim.
465
512
 
466
513
  ## Recommended next extensions
467
514
 
@@ -473,15 +520,12 @@ If performance is going to remain a central public claim, the benchmark suite sh
473
520
  4. More explicit GLMM post-fit benchmarks beyond fitting, prediction, and Wald intervals.
474
521
  5. Benchmarks that isolate optimizer iteration cost separately from formula parsing and matrix construction.
475
522
 
476
- ## Recommendation for this repo today
477
-
478
- The current benchmarking is meaningful, but not comprehensive.
523
+ ## Maintaining performance claims
479
524
 
480
- That means:
525
+ Use Criterion as a regression guard and the fair harness for external timing.
526
+ Keep raw artifacts, case names, code revision, environment, and methodology
527
+ with each claim. Refresh only the evidence affected by a change.
481
528
 
482
- - yes, keep performance as a **completion criterion** for Rust-native workflows, not only regression tracking (Criterion + fair harness)
483
- - yes, keep **LMM fit optimization** as a regression guard: axis (3) requires **<1.0×** Julia on tier-A `cold_fit` ([2026-07-22 full reference](benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json))
484
- - yes, tier-A hot `fit_prepared` and cold `lmer()` both beat MixedModels.jl throughout the current full reference; re-run before citing new speed claims
485
- - no, Julia bindings to `lme-rs` are not justified by speed — see [fair Rust vs Julia results](BENCHMARKS.md#fair-rust-vs-julia-reference-results)
486
- - yes, run benchmarks for performance-sensitive changes before release
487
- - yes, extend the benchmark surface (GLMM fit-only, prediction sweeps) as optimization work proceeds
529
+ Completion thresholds are governed by [the locked manifest](completion_manifest.json)
530
+ and [coverage policy](BENCHMARK_COVERAGE.md). A faster microbenchmark or revised
531
+ description alone does not satisfy a broader completion criterion.
@@ -1,8 +1,14 @@
1
1
  # Benchmark coverage map
2
2
 
3
+ [Documentation](docs/README.md) · [Benchmark guide](BENCHMARKS.md) · [Optimization notes](OPTIMIZATION.md)
4
+
3
5
  This file maps **which parts of `lme-rs` have external performance references** (not just Rust-only Criterion benches). Use it to ground [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md) axis (3) and [USABILITY.md](USABILITY.md) performance posture.
4
6
 
5
- **Last assessed:** 2026-08-14
7
+ **Evidence assessment:** 2026-08-15. The catalog below reports dated artifacts,
8
+ not measurements rerun during documentation maintenance.
9
+
10
+ **Jump to:** [Harness tiers](#harness-tiers) · [Case catalog](#tier-a-case-catalog) ·
11
+ [Commands](#running-benchmarks) · [Claim policy](#what-each-completion-row-may-claim)
6
12
 
7
13
  ---
8
14
 
@@ -23,7 +29,7 @@ This file maps **which parts of `lme-rs` have external performance references**
23
29
 
24
30
  Default target: **Rust median < 1.0× Julia median** on `cold_fit` for tier-A LMM cases on the reference workstation (strictly faster than MixedModels.jl).
25
31
 
26
- Prior milestones: **≤ 2×** (through 2026-07-08) while crossed/nested were multi× slower; **≤ 1.5×** (2026-07-09–15); selected crossed/nested strict passes on 2026-07-16. The [2026-07-22 full tier-A reference](benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json) records all 12 cases at the current **1.0×** gate with no failures.
32
+ Prior milestones: **≤ 2×** (through 2026-07-08) while crossed/nested were multi× slower; **≤ 1.5×** (2026-07-09–15); selected crossed/nested strict passes on 2026-07-16. The [2026-07-22 full tier-A reference](benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json) records all 12 cases at the **1.0×** gate on that revision. LMM rows remain the current LMM evidence (`math.rs` / `optimizer.rs` unchanged); the two GLMM rows predate Gamma PIRLS phi profiling and multivariate AGQ-in-θ.
27
33
 
28
34
  ```powershell
29
35
  python scripts/run_fair_rust_julia_benchmark.py --implementations rust,julia --with-phases --repeats 10
@@ -47,18 +53,18 @@ Hot-path target (batch / CV): **`fit_prepared` ≤ ~1× Julia `fit`** when `--wi
47
53
  | `large_random_slopes_100k` | LMM | Synthetic (100k obs; 2k groups) | MixedModels.jl | **0.898×** | **0.646×** | Showcase: correlated intercept/slope, 3 θ; linear cache setup |
48
54
  | `crossed_20k` | LMM | Synthetic | MixedModels.jl | **0.879×** | **0.677×** | Direct two-factor Gram + allocation-free blocked gate |
49
55
  | `nested_10k` | LMM | Synthetic | MixedModels.jl | **0.961×** | **0.492×** | Direct slash design + membership Gram |
50
- | `cbpp_binomial_ml` | GLMM | Real binomial | MixedModels.jl GLMM | **0.749×** | N/A | Laplace; not R `nAGQ`-in-θ |
51
- | `grouseticks_poisson_ml` | GLMM | Real Poisson | MixedModels.jl GLMM | **0.033×** | N/A | Laplace |
56
+ | `cbpp_binomial_ml` | GLMM | Real binomial | MixedModels.jl GLMM | **0.749×** | N/A | Historical Laplace timing; rerun after GLMM changes |
57
+ | `grouseticks_poisson_ml` | GLMM | Real Poisson | MixedModels.jl GLMM | **0.033×** | N/A | Historical Laplace timing; rerun after GLMM changes |
52
58
 
53
59
  Cases not in tier A (no fair MixedModels.jl fit timing yet):
54
60
 
55
61
  | Area | Rust bench | Comparable | Status |
56
62
  |:-----|:-----------|:-----------|:-------|
57
- | `nlmer` / Orange | Golden parity + [`scripts/run_external_timings.py`](scripts/run_external_timings.py) | `lme4::nlmer` | **External R timing** (not Julia) |
58
- | GLMM AGQ (`n_agq > 1`) | Criterion | R semantics differ | **No** apples-to-apples |
63
+ | `nlmer` / Orange | Golden parity + [`scripts/run_external_timings.py`](scripts/run_external_timings.py) | `lme4::nlmer` | **External R timing** (not Julia; inspect the dated artifact) |
64
+ | GLMM AGQ (`n_agq > 1`) | Criterion | Match integration and optimizer settings | **No** matched external timing in this tier |
59
65
  | Post-fit inference (KR, ANOVA, predict) | Criterion + external harness | `lmerTest` when installed | **External R timing** for KR / Satterthwaite ANOVA |
60
66
  | LMM estimated marginal means / pairs | Criterion (`inference/emmeans_*`) | R `emmeans` correctness fixture | **Rust microbenchmark**; no cross-language speed claim |
61
- | Python `lme_python` FFI | External harness | Rust `lmer` | **External Python timing** |
67
+ | Python `lme_python` FFI | External harness | Rust `lmer` | **External Python timing** (inspect the dated artifact) |
62
68
  | `lm` / `lm_df` | Minimal | R `lm` | **No** (usually negligible) |
63
69
 
64
70
  The 2026-08-14 Windows Criterion run measured `inference/emmeans_reference_grid` at **42.5–43.1 µs** and `inference/emmeans_pairs_tukey` at **52.9–56.8 µs** on the pastes fixture. These are post-fit operations and do not warrant a specialized cache at the current scale.
@@ -71,7 +77,7 @@ The 2026-08-14 Windows Criterion run measured `inference/emmeans_reference_grid`
71
77
  |:------------|:----------------------------|
72
78
  | **1 LMM** | Tier-A LMM cases meet `cold_fit` target (or documented exception) |
73
79
  | **2 GLMM** | Tier-A GLMM cases measured; do not infer from LMM row 13 alone |
74
- | **13 Throughput** | Fraction of tier-A cases at target; see case table above |
80
+ | **13 Throughput** | Named LMM cases at target from a **current** artifact; full-suite-including-GLMM needs a rerun after GLMM fit-math changes |
75
81
  | **4 Inference** | Correctness / API only unless tier added |
76
82
 
77
83
  ---
@@ -122,4 +128,6 @@ A dated reference JSON lives under [`benchmarks/`](benchmarks/). Use `--rust-onl
122
128
  1. After optimization work on a workflow, add or refresh its tier-A case.
123
129
  2. Record medians in [BENCHMARKS.md](BENCHMARKS.md) and/or commit a dated reference JSON under [`benchmarks/`](benchmarks/).
124
130
  3. Update **Measured** / **Re-run** cells in the case catalog above.
125
- 4. Adjust [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md) row **13** only from tier-A evidence.
131
+ 4. Update [completion_manifest.json](completion_manifest.json) and its supporting
132
+ evidence only when the existing locked scope is met. Keep generated report
133
+ markers aligned and run `task completion:check`; do not edit percentages by hand.
@@ -5,7 +5,59 @@ 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]
8
+ ## [0.2.3] - 2026-09-09
9
+
10
+ ### Fixed
11
+
12
+ - Preserve offsets and observation weights throughout Satterthwaite and Kenward-Roger inference.
13
+ - Use one Kenward-Roger covariance adjustment for marginal and joint tests, confidence
14
+ intervals, estimated marginal means, and multiple comparisons; retain precision
15
+ for very small p-values and reject nonfinite contrast inputs.
16
+ - Respect precision weights in observation-level and cluster-robust standard errors.
17
+ - Keep nested grouping labels distinct when components contain underscores or backslashes.
18
+ - Honor requested grouped cross-validation fold counts, accept supported numeric
19
+ response types, and reject missing split labels and invalid responses.
20
+ - Preserve predictors and responses named `intercept` in formula models.
21
+ - Reject nonfinite confidence levels and mismatched or missing robust-inference data.
22
+ - Leave uncertainty unavailable when an OLS fit has no residual degrees of freedom.
23
+
24
+
25
+ - Preserve formula offsets in population/conditional predictions and OLS fitting.
26
+ - Preserve precision weights in Gaussian simulation and bootstrap refits; weighted
27
+ residual resampling uses standardized residuals.
28
+ - Refine two-parameter LMM grid minima before reporting optimizer convergence.
29
+ - Honor nonlinear iteration budgets and expose failed numerical stages. Nonlinear
30
+ ML fitting now refines covariance and population parameters jointly and includes
31
+ the full Gaussian likelihood constant; reported likelihood/AIC/BIC may change.
32
+ - Compute OLS coefficients and covariance from one QR decomposition with rank checks.
33
+
34
+ ### Added
35
+
36
+ - Rust `FitControl`, `FitDiagnostics`, `ModelSpec`, and `ExecutionContext`; Python
37
+ `FitControl`, `fit.diagnostics`, and `prepared.fit(y=..., control=...)`.
38
+ - Independent response workspaces with shared immutable LMM/GLMM designs, retained
39
+ GLMM sparse factorization structure, and group-block nonlinear normal equations.
40
+ - Formula-based Python IPC column projection and interpreter release during native
41
+ fitting, prediction, bootstrap, and profile intervals.
42
+
43
+ ### Compatibility
44
+
45
+ - Existing fitting functions retain their defaults. Rust consumers constructing
46
+ `LmeFit` literals must add `diagnostics: None`; numerical design fields exposed
47
+ through `LmmData`/`GlmmData` are now immutable shared data.
48
+ - A nonconverged fit remains inspectable, but Wald intervals reject it. Set
49
+ `require_convergence` to reject unsuccessful LMM/GLMM fits immediately.
50
+ - Resampling no longer changes process-wide BLAS/OpenMP environment variables.
51
+ `n_jobs=None` uses the current Rayon context; callers own backend thread settings.
52
+
53
+ ### Changed
54
+
55
+ - Reworked documentation navigation, Rust/Python onboarding, example discovery,
56
+ troubleshooting, workflow scope, and the contributor/release runbooks. Corrected
57
+ prepared-fit, bootstrap, calibration-bound, and benchmark-evidence descriptions.
58
+ - Reevaluated the repository completion score. The 2026-08-14 **100% (236/236)** headline overstated remaining stretch-item depth, omitted NLMM from the denominator, and could be satisfied by file existence alone. Locked criterion `scope` strings, reopened partial/stale/substituted items, added NLMM as row 14, and published **91% (235/258)**. See [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md).
59
+ - Reorganized the GitHub README around the existing banner: table of contents, capability and documentation tables, dual Rust/Python install and quick start, an `lme4` mapping, and structured limitation notes.
60
+ - Rebuilt the GitHub Pages [benchmark dashboard](https://x4g4p3x.github.io/lme-rs/benchmarks/) around the fair MixedModels.jl harness and checked-in external timings, and restored Pages deployment.
9
61
 
10
62
  ## [0.2.2] - 2026-08-14
11
63
 
@@ -1,4 +1,10 @@
1
- # CI performance
1
+ # CI performance history
2
+
3
+ [Documentation](docs/README.md) · [Current CI behavior](CONTRIBUTING.md#github-actions) · [Runner reference](scripts/ci/README.md)
4
+
5
+ These are historical measurements from the linked runs, not current duration
6
+ estimates. Use the workflow files and shared runner for the current validation
7
+ contract. Retain both cold and warm runs when adding a new comparison.
2
8
 
3
9
  This document records hosted-CI timing evidence and the optimizations that are
4
10
  safe to depend on. Times are GitHub Actions job timestamps, not local estimates.