lme-python 0.2.2__tar.gz → 0.2.4__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 (390) hide show
  1. lme_python-0.2.4/.github/FUNDING.yml +1 -0
  2. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/audit.yml +2 -2
  3. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/benchmarks.yml +76 -0
  4. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/cache-prime.yml +3 -3
  5. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/ci.yml +9 -7
  6. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/fuzz-smoke.yml +1 -1
  7. lme_python-0.2.4/.github/workflows/pages.yml +56 -0
  8. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/python-release.yml +1 -1
  9. lme_python-0.2.4/AGENTS.md +148 -0
  10. {lme_python-0.2.2 → lme_python-0.2.4}/BENCHMARKS.md +73 -29
  11. {lme_python-0.2.2 → lme_python-0.2.4}/BENCHMARK_COVERAGE.md +17 -9
  12. {lme_python-0.2.2 → lme_python-0.2.4}/CHANGELOG.md +101 -0
  13. {lme_python-0.2.2 → lme_python-0.2.4}/CI_PERFORMANCE.md +7 -1
  14. lme_python-0.2.4/CONTRIBUTING.md +235 -0
  15. {lme_python-0.2.2 → lme_python-0.2.4}/Cargo.lock +1 -1
  16. {lme_python-0.2.2 → lme_python-0.2.4}/Cargo.toml +4 -1
  17. {lme_python-0.2.2 → lme_python-0.2.4}/GUIDE.md +100 -41
  18. {lme_python-0.2.2 → lme_python-0.2.4}/OPTIMIZATION.md +88 -4
  19. lme_python-0.2.4/PKG-INFO +127 -0
  20. lme_python-0.2.4/README.md +220 -0
  21. lme_python-0.2.4/RELEASING.md +157 -0
  22. lme_python-0.2.4/REPO_COMPLETION_BY_AREA.md +132 -0
  23. {lme_python-0.2.2 → lme_python-0.2.4}/Taskfile.yml +15 -0
  24. lme_python-0.2.4/USABILITY.md +110 -0
  25. lme_python-0.2.4/benchmarks/fair-rust-julia-reference-2026-09-09-refactor.json +1693 -0
  26. lme_python-0.2.4/benchmarks/refactor-2026-09-09.json +1854 -0
  27. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/COMPARISONS.md +25 -4
  28. lme_python-0.2.4/completion_manifest.json +159 -0
  29. lme_python-0.2.4/docs/CALO_CALIBRATION.md +98 -0
  30. lme_python-0.2.4/docs/EXAMPLES.md +86 -0
  31. lme_python-0.2.4/docs/README.md +69 -0
  32. lme_python-0.2.4/docs/TROUBLESHOOTING.md +91 -0
  33. lme_python-0.2.4/docs/benchmarks/app.js +397 -0
  34. lme_python-0.2.4/docs/benchmarks/data/latest.json +834 -0
  35. lme_python-0.2.4/docs/benchmarks/index.html +133 -0
  36. {lme_python-0.2.2 → lme_python-0.2.4}/docs/benchmarks/styles.css +62 -3
  37. {lme_python-0.2.2 → lme_python-0.2.4}/docs/index.html +1 -1
  38. lme_python-0.2.4/docs/rustdoc.css +157 -0
  39. lme_python-0.2.4/docs/rustdoc.md +131 -0
  40. {lme_python-0.2.2 → lme_python-0.2.4}/lefthook.yml +4 -0
  41. lme_python-0.2.4/lme-rs-icon.png +0 -0
  42. lme_python-0.2.4/lme-rs.png +0 -0
  43. {lme_python-0.2.2 → lme_python-0.2.4}/python/Cargo.lock +2 -2
  44. {lme_python-0.2.2 → lme_python-0.2.4}/python/Cargo.toml +1 -1
  45. {lme_python-0.2.2 → lme_python-0.2.4}/python/PYTHON_GUIDE.md +163 -112
  46. lme_python-0.2.4/python/README.md +104 -0
  47. lme_python-0.2.4/python/examples/plotting_demo/README.md +101 -0
  48. lme_python-0.2.4/python/examples/verification_project/README.md +51 -0
  49. {lme_python-0.2.2 → lme_python-0.2.4}/python/lme_python.pyi +48 -10
  50. lme_python-0.2.4/python/src/controls.rs +61 -0
  51. lme_python-0.2.4/python/src/fitting.rs +597 -0
  52. lme_python-0.2.4/python/src/input.rs +107 -0
  53. {lme_python-0.2.2 → lme_python-0.2.4}/python/src/lib.rs +131 -727
  54. lme_python-0.2.4/python/tests/test_model_contracts.py +133 -0
  55. {lme_python-0.2.2 → lme_python-0.2.4}/python/uv.lock +4 -4
  56. lme_python-0.2.4/scripts/build_benchmark_site.py +508 -0
  57. lme_python-0.2.4/scripts/ci/README.md +55 -0
  58. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/check_completion_score.py +36 -3
  59. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/lme_ci.py +78 -2
  60. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/explorations/README.md +5 -1
  61. {lme_python-0.2.2 → lme_python-0.2.4}/src/bootstrap.rs +108 -103
  62. {lme_python-0.2.2 → lme_python-0.2.4}/src/contrast.rs +117 -26
  63. {lme_python-0.2.2 → lme_python-0.2.4}/src/cv.rs +32 -86
  64. {lme_python-0.2.2 → lme_python-0.2.4}/src/ddf.rs +1 -1
  65. lme_python-0.2.4/src/display.rs +283 -0
  66. {lme_python-0.2.2 → lme_python-0.2.4}/src/emmeans.rs +8 -20
  67. lme_python-0.2.4/src/execution.rs +48 -0
  68. {lme_python-0.2.2 → lme_python-0.2.4}/src/formula.rs +2 -1
  69. {lme_python-0.2.2 → lme_python-0.2.4}/src/glmm_math.rs +149 -74
  70. {lme_python-0.2.2 → lme_python-0.2.4}/src/intercept_blocked.rs +144 -9
  71. lme_python-0.2.4/src/kenward_roger.rs +81 -0
  72. {lme_python-0.2.2 → lme_python-0.2.4}/src/kr_modcomp.rs +72 -77
  73. {lme_python-0.2.2 → lme_python-0.2.4}/src/kr_vcov_adj.rs +7 -12
  74. {lme_python-0.2.2 → lme_python-0.2.4}/src/lib.rs +210 -596
  75. {lme_python-0.2.2 → lme_python-0.2.4}/src/math.rs +99 -84
  76. {lme_python-0.2.2 → lme_python-0.2.4}/src/mcp.rs +33 -7
  77. lme_python-0.2.4/src/model.rs +240 -0
  78. {lme_python-0.2.2 → lme_python-0.2.4}/src/model_matrix.rs +62 -60
  79. lme_python-0.2.4/src/nlmm/block_solve.rs +132 -0
  80. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/fit.rs +263 -140
  81. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/mod.rs +1 -0
  82. lme_python-0.2.4/src/ols.rs +33 -0
  83. {lme_python-0.2.2 → lme_python-0.2.4}/src/optimizer.rs +138 -101
  84. {lme_python-0.2.2 → lme_python-0.2.4}/src/perf_diag.rs +1 -1
  85. lme_python-0.2.4/src/predict.rs +184 -0
  86. lme_python-0.2.4/src/prepared.rs +76 -0
  87. {lme_python-0.2.2 → lme_python-0.2.4}/src/profile_ci.rs +6 -11
  88. {lme_python-0.2.2 → lme_python-0.2.4}/src/robust.rs +17 -2
  89. {lme_python-0.2.2 → lme_python-0.2.4}/src/satterthwaite.rs +3 -10
  90. {lme_python-0.2.2 → lme_python-0.2.4}/src/simulate.rs +47 -22
  91. {lme_python-0.2.2 → lme_python-0.2.4}/src/studentized_range.rs +15 -0
  92. {lme_python-0.2.2 → lme_python-0.2.4}/tests/ci_consolidated.rs +13 -0
  93. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_anova.rs +1 -0
  94. lme_python-0.2.4/tests/test_bug_hunt.rs +353 -0
  95. lme_python-0.2.4/tests/test_bug_hunt_edges.rs +255 -0
  96. lme_python-0.2.4/tests/test_bug_hunt_more.rs +212 -0
  97. lme_python-0.2.4/tests/test_bug_hunt_numerics.rs +287 -0
  98. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_coverage_gaps.rs +2 -1
  99. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_failure_modes.rs +14 -14
  100. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_formula_stress.rs +1 -0
  101. lme_python-0.2.4/tests/test_inference_guards.rs +187 -0
  102. lme_python-0.2.4/tests/test_model_contracts.rs +333 -0
  103. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_predict.rs +4 -3
  104. lme_python-0.2.2/AGENTS.md +0 -112
  105. lme_python-0.2.2/CONTRIBUTING.md +0 -220
  106. lme_python-0.2.2/PKG-INFO +0 -67
  107. lme_python-0.2.2/README.md +0 -113
  108. lme_python-0.2.2/RELEASING.md +0 -273
  109. lme_python-0.2.2/REPO_COMPLETION_BY_AREA.md +0 -110
  110. lme_python-0.2.2/USABILITY.md +0 -185
  111. lme_python-0.2.2/completion_manifest.json +0 -20
  112. lme_python-0.2.2/docs/CALO_CALIBRATION.md +0 -124
  113. lme_python-0.2.2/docs/benchmarks/app.js +0 -207
  114. lme_python-0.2.2/docs/benchmarks/index.html +0 -92
  115. lme_python-0.2.2/python/README.md +0 -44
  116. lme_python-0.2.2/python/examples/plotting_demo/README.md +0 -129
  117. lme_python-0.2.2/python/examples/verification_project/README.md +0 -42
  118. lme_python-0.2.2/scripts/build_benchmark_site.py +0 -210
  119. lme_python-0.2.2/scripts/ci/README.md +0 -31
  120. lme_python-0.2.2/src/kenward_roger.rs +0 -357
  121. {lme_python-0.2.2 → lme_python-0.2.4}/.JuliaFormatter.toml +0 -0
  122. {lme_python-0.2.2 → lme_python-0.2.4}/.github/dependabot.yml +0 -0
  123. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/crate-publish-dry-run.yml +0 -0
  124. {lme_python-0.2.2 → lme_python-0.2.4}/.github/workflows/repo-metadata.yml +0 -0
  125. {lme_python-0.2.2 → lme_python-0.2.4}/.gitignore +0 -0
  126. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSE +0 -0
  127. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSES/Apache-2.0.txt +0 -0
  128. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSES/BSD-3-Clause-OpenBLAS.txt +0 -0
  129. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSES/GPL-2.0-or-later.txt +0 -0
  130. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSES/Intel-Simplified-Software-License.txt +0 -0
  131. {lme_python-0.2.2 → lme_python-0.2.4}/LICENSES/LGPL-2.1-only.txt +0 -0
  132. {lme_python-0.2.2 → lme_python-0.2.4}/RELINKING.md +0 -0
  133. {lme_python-0.2.2 → lme_python-0.2.4}/THIRD_PARTY_NOTICES.md +0 -0
  134. {lme_python-0.2.2 → lme_python-0.2.4}/benches/bench_load_production.rs +0 -0
  135. {lme_python-0.2.2 → lme_python-0.2.4}/benches/bench_math.rs +0 -0
  136. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/external-nlmm-inference-python-timings-2026-08-14.json +0 -0
  137. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-04.json +0 -0
  138. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-06.json +0 -0
  139. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-08.json +0 -0
  140. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-direct-intercept-gram.json +0 -0
  141. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-full-lmm.json +0 -0
  142. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-glmm.json +0 -0
  143. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-large-intercept-setup.json +0 -0
  144. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes-linear-cache.json +0 -0
  145. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes.json +0 -0
  146. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09-sleepstudy-slopes.json +0 -0
  147. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-09.json +0 -0
  148. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-10-cold-setup-followup.json +0 -0
  149. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-16-cold-fit-lt1.json +0 -0
  150. {lme_python-0.2.2 → lme_python-0.2.4}/benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json +0 -0
  151. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/bench_external_timings.R +0 -0
  152. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/bench_fair_julia_perf.jl +0 -0
  153. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/bench_fair_julia_timing.jl +0 -0
  154. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/bench_fair_rust_julia.rs +0 -0
  155. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/bench_perf_breakdown.rs +0 -0
  156. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/categorical_anova.R +0 -0
  157. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/categorical_anova.jl +0 -0
  158. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/categorical_anova.py +0 -0
  159. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/categorical_anova.rs +0 -0
  160. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/comparison_chart.png +0 -0
  161. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_cbpp.R +0 -0
  162. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_cbpp.jl +0 -0
  163. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_cbpp.py +0 -0
  164. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_cbpp.rs +0 -0
  165. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_gaussian.R +0 -0
  166. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_gaussian.jl +0 -0
  167. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_gaussian.py +0 -0
  168. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_gaussian.rs +0 -0
  169. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_grouseticks.R +0 -0
  170. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_grouseticks.jl +0 -0
  171. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_grouseticks.py +0 -0
  172. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/glmm_grouseticks.rs +0 -0
  173. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmer_weighted.R +0 -0
  174. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmer_weighted.jl +0 -0
  175. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmer_weighted.py +0 -0
  176. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmer_weighted.rs +0 -0
  177. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_dyestuff.R +0 -0
  178. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_dyestuff.jl +0 -0
  179. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_dyestuff.py +0 -0
  180. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_dyestuff.rs +0 -0
  181. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_pastes.R +0 -0
  182. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_pastes.jl +0 -0
  183. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_pastes.py +0 -0
  184. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_pastes.rs +0 -0
  185. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_penicillin.R +0 -0
  186. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_penicillin.jl +0 -0
  187. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_penicillin.py +0 -0
  188. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/lmm_penicillin.rs +0 -0
  189. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/nlmm_sspower.R +0 -0
  190. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_offset.R +0 -0
  191. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_offset.jl +0 -0
  192. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_probit.R +0 -0
  193. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_probit.jl +0 -0
  194. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_weighted.R +0 -0
  195. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/glmm_weighted.jl +0 -0
  196. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/parity_r_setup.R +0 -0
  197. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/sleepstudy_offset.R +0 -0
  198. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity/sleepstudy_offset.jl +0 -0
  199. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/parity_export.rs +0 -0
  200. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/pastes_emmeans.R +0 -0
  201. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/pastes_glht.R +0 -0
  202. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/r_setup.R +0 -0
  203. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy.R +0 -0
  204. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy.jl +0 -0
  205. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy.py +0 -0
  206. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy.rs +0 -0
  207. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy_confint_profile.R +0 -0
  208. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy_ml.R +0 -0
  209. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy_ml.jl +0 -0
  210. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy_ml.py +0 -0
  211. {lme_python-0.2.2 → lme_python-0.2.4}/comparisons/sleepstudy_ml.rs +0 -0
  212. {lme_python-0.2.2 → lme_python-0.2.4}/examples/batch_sspower_cpu.rs +0 -0
  213. {lme_python-0.2.2 → lme_python-0.2.4}/examples/bench_external_timings.rs +0 -0
  214. {lme_python-0.2.2 → lme_python-0.2.4}/examples/dump_cascade_fixtures.rs +0 -0
  215. {lme_python-0.2.2 → lme_python-0.2.4}/examples/explore_formula_ast.rs +0 -0
  216. {lme_python-0.2.2 → lme_python-0.2.4}/examples/explore_mcp_adjust.rs +0 -0
  217. {lme_python-0.2.2 → lme_python-0.2.4}/examples/explore_theta_grid.rs +0 -0
  218. {lme_python-0.2.2 → lme_python-0.2.4}/examples/nlmm_fixture_dump.rs +0 -0
  219. {lme_python-0.2.2 → lme_python-0.2.4}/legal/dependency-license-exceptions.json +0 -0
  220. {lme_python-0.2.2 → lme_python-0.2.4}/legal/fixture-provenance.json +0 -0
  221. {lme_python-0.2.2 → lme_python-0.2.4}/mise.toml +0 -0
  222. {lme_python-0.2.2 → lme_python-0.2.4}/pyproject.toml +0 -0
  223. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/glmer_cbpp.py +0 -0
  224. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/glmer_grouseticks.py +0 -0
  225. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/lm_basics.py +0 -0
  226. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/lmer_sleepstudy.py +0 -0
  227. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/model_comparison.py +0 -0
  228. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/.gitignore +0 -0
  229. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/compare_plots.py +0 -0
  230. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/figure_specs.py +0 -0
  231. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/find_rscript.py +0 -0
  232. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/numeric_overlay.py +0 -0
  233. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/paths.py +0 -0
  234. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/plot_demo.py +0 -0
  235. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/plot_r.R +0 -0
  236. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/plotting_demo/run_all.py +0 -0
  237. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/verification_project/conftest.py +0 -0
  238. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/verification_project/parity.py +0 -0
  239. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/verification_project/paths.py +0 -0
  240. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/verification_project/run.py +0 -0
  241. {lme_python-0.2.2 → lme_python-0.2.4}/python/examples/verification_project/test_parity.py +0 -0
  242. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_api_parity.py +0 -0
  243. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_basic.py +0 -0
  244. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_cv.py +0 -0
  245. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_dataframe_inputs.py +0 -0
  246. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_golden_parity.py +0 -0
  247. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_nlmer_custom_mean.py +0 -0
  248. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_simulate_scale.py +0 -0
  249. {lme_python-0.2.2 → lme_python-0.2.4}/python/tests/test_wheel_notices.py +0 -0
  250. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/check_legal_compliance.py +0 -0
  251. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/julia_format.jl +0 -0
  252. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/package_wheel_notices.py +0 -0
  253. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/r_format.R +0 -0
  254. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/ci/run.sh +0 -0
  255. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/dump_dyestuff.R +0 -0
  256. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/dump_pastes.R +0 -0
  257. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/local_ci.ps1 +0 -0
  258. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/local_ci.sh +0 -0
  259. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/make_penicillin_csv.py +0 -0
  260. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/plot_comparisons.py +0 -0
  261. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/run_cross_language_benchmarks.py +0 -0
  262. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/run_external_timings.py +0 -0
  263. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/run_fair_rust_julia_benchmark.py +0 -0
  264. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/run_perf_breakdown.py +0 -0
  265. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/sync_github_repo_metadata.py +0 -0
  266. {lme_python-0.2.2 → lme_python-0.2.4}/scripts/verify_cross_language_parity.py +0 -0
  267. {lme_python-0.2.2 → lme_python-0.2.4}/src/anova.rs +0 -0
  268. {lme_python-0.2.2 → lme_python-0.2.4}/src/anova_contrasts.rs +0 -0
  269. {lme_python-0.2.2 → lme_python-0.2.4}/src/basis.rs +0 -0
  270. {lme_python-0.2.2 → lme_python-0.2.4}/src/family.rs +0 -0
  271. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/formula.rs +0 -0
  272. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/mean.rs +0 -0
  273. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/mean_fn.rs +0 -0
  274. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/predict.rs +0 -0
  275. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/re_cov.rs +0 -0
  276. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/self_start.rs +0 -0
  277. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssasymp.rs +0 -0
  278. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssasympoff.rs +0 -0
  279. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssasymporig.rs +0 -0
  280. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssbiexp.rs +0 -0
  281. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssfpl.rs +0 -0
  282. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssgompertz.rs +0 -0
  283. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/sslogis.rs +0 -0
  284. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssmicmen.rs +0 -0
  285. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/sspower.rs +0 -0
  286. {lme_python-0.2.2 → lme_python-0.2.4}/src/nlmm/ssweibull.rs +0 -0
  287. {lme_python-0.2.2 → lme_python-0.2.4}/src/quadrature.rs +0 -0
  288. {lme_python-0.2.2 → lme_python-0.2.4}/tests/categorical_anova_test.rs +0 -0
  289. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/cbpp_binary.csv +0 -0
  290. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/cbpp_binary_weighted.csv +0 -0
  291. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/dyestuff.csv +0 -0
  292. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/dyestuff_intercept_reml.json +0 -0
  293. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_binomial.json +0 -0
  294. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_binomial_agq.json +0 -0
  295. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_binomial_cloglog.json +0 -0
  296. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_binomial_probit.json +0 -0
  297. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_binomial_weighted.json +0 -0
  298. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_gamma_dyestuff.json +0 -0
  299. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_poisson.json +0 -0
  300. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/glmm_poisson_offset.json +0 -0
  301. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/golden_parity_manifest.json +0 -0
  302. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/grouseticks.csv +0 -0
  303. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/intercept_only.json +0 -0
  304. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/mock_crossed.json +0 -0
  305. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/mock_ml.json +0 -0
  306. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/orange.csv +0 -0
  307. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/orange_nlmer.json +0 -0
  308. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/pastes.csv +0 -0
  309. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/pastes_cask_reml.json +0 -0
  310. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/pastes_emmeans.json +0 -0
  311. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/pastes_glht_tukey.json +0 -0
  312. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/penicillin.csv +0 -0
  313. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/penicillin.json +0 -0
  314. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/r_edge_case_matrix.json +0 -0
  315. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/random_slopes.json +0 -0
  316. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sleepstudy.csv +0 -0
  317. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sleepstudy_confint_profile.json +0 -0
  318. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sleepstudy_confint_profile_vc.json +0 -0
  319. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sleepstudy_offset_reml.json +0 -0
  320. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssasymp_synthetic.csv +0 -0
  321. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssasympoff_nlmer.json +0 -0
  322. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssasympoff_synthetic.csv +0 -0
  323. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssasymporig_nlmer.json +0 -0
  324. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssasymporig_synthetic.csv +0 -0
  325. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssbiexp_nlmer.json +0 -0
  326. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssbiexp_synthetic.csv +0 -0
  327. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssfol_nlmer.json +0 -0
  328. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssfpl_nlmer.json +0 -0
  329. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssfpl_synthetic.csv +0 -0
  330. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssgompertz_nlmer.json +0 -0
  331. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssgompertz_synthetic.csv +0 -0
  332. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssmicmen_nlmer.json +0 -0
  333. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssmicmen_synthetic.csv +0 -0
  334. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sspower_nlmer.json +0 -0
  335. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/sspower_synthetic.csv +0 -0
  336. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssweibull_nlmer.json +0 -0
  337. {lme_python-0.2.2 → lme_python-0.2.4}/tests/data/ssweibull_synthetic.csv +0 -0
  338. {lme_python-0.2.2 → lme_python-0.2.4}/tests/gen_nlmm_extras.R +0 -0
  339. {lme_python-0.2.2 → lme_python-0.2.4}/tests/generate_mock_data.py +0 -0
  340. {lme_python-0.2.2 → lme_python-0.2.4}/tests/generate_nlmm_fixtures.R +0 -0
  341. {lme_python-0.2.2 → lme_python-0.2.4}/tests/generate_r_edge_case_matrix.R +0 -0
  342. {lme_python-0.2.2 → lme_python-0.2.4}/tests/generate_test_data.R +0 -0
  343. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_anova_types.rs +0 -0
  344. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_bootstrap.rs +0 -0
  345. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_conditional_real.rs +0 -0
  346. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_confint_profile.rs +0 -0
  347. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_confint_simulate.rs +0 -0
  348. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_contrast.rs +0 -0
  349. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_coverage_edge_cases.rs +0 -0
  350. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_crossed_mock.rs +0 -0
  351. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_cv.rs +0 -0
  352. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_e2e_lmer.rs +0 -0
  353. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_edge_cases.rs +0 -0
  354. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_emmeans.rs +0 -0
  355. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_errors.rs +0 -0
  356. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_explorations.rs +0 -0
  357. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_external_timings_artifact.rs +0 -0
  358. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_formula.rs +0 -0
  359. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_gaps.rs +0 -0
  360. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_glmm.rs +0 -0
  361. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_glmm_links.rs +0 -0
  362. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_glmm_offset_grouseticks.rs +0 -0
  363. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_glmm_weighted.rs +0 -0
  364. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_golden_parity.rs +0 -0
  365. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_intercept_only.rs +0 -0
  366. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_kenward_roger.rs +0 -0
  367. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_kr_modcomp_pastes.rs +0 -0
  368. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_mcp.rs +0 -0
  369. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_ml_optimization.rs +0 -0
  370. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_agq.rs +0 -0
  371. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_bounds.rs +0 -0
  372. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_custom_mean.rs +0 -0
  373. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_orange.rs +0 -0
  374. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_orange_multi_re.rs +0 -0
  375. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_self_start.rs +0 -0
  376. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_ss_new_means.rs +0 -0
  377. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_ssasymp.rs +0 -0
  378. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_ssasymp_off_orig.rs +0 -0
  379. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_ssfol.rs +0 -0
  380. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_ssmicmen.rs +0 -0
  381. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_nlmm_sspower.rs +0 -0
  382. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_no_intercept.rs +0 -0
  383. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_numerical_parity.rs +0 -0
  384. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_production_load.rs +0 -0
  385. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_r_edge_case_matrix.rs +0 -0
  386. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_random_slopes.rs +0 -0
  387. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_robust.rs +0 -0
  388. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_sandwich_math.R +0 -0
  389. {lme_python-0.2.2 → lme_python-0.2.4}/tests/test_satterthwaite.rs +0 -0
  390. {lme_python-0.2.2 → lme_python-0.2.4}/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 }}
@@ -0,0 +1,148 @@
1
+ # Agent guide
2
+
3
+ This is a Rust statistical library (`lme-rs`) with PyO3/maturin Python bindings
4
+ (`lme_python`). There is no application server to start. Run commands from the
5
+ repository root unless stated otherwise.
6
+
7
+ ## Start here
8
+
9
+ 1. Inspect `git status --short --branch` and the relevant diff. Preserve unrelated
10
+ changes; do not reset, stash, switch branches, or update dependencies as routine setup.
11
+ 2. Use `rg` and the map below to find the implementation and existing tests. Read
12
+ relevant guides rather than scanning the whole repository before a focused fix.
13
+ 3. Choose required checks before editing. Reproduce bugs with focused tests, fix
14
+ the cause, and verify both the regression and affected behavior.
15
+ 4. Update [CHANGELOG.md](CHANGELOG.md) under `Unreleased` for user-visible fixes,
16
+ features, and compatibility changes. Instruction-only edits need no entry.
17
+ 5. Review the final diff. Report changes, validation, and failures/skips/limitations;
18
+ do not claim unrun checks.
19
+
20
+ ## Code map
21
+
22
+ | Area | Start with |
23
+ |---|---|
24
+ | Public API, contracts, reusable fits | `src/lib.rs`, `src/model.rs`, `src/prepared.rs` |
25
+ | Formulas and design matrices | `src/formula.rs`, `src/model_matrix.rs`, `src/basis.rs` |
26
+ | LMM / GLMM / nonlinear fitting | `src/math.rs`, `src/optimizer.rs`, `src/intercept_blocked.rs` / `src/glmm_math.rs`, `src/family.rs` / `src/nlmm/` |
27
+ | Inference and post-fit behavior | `src/contrast.rs`, `src/ddf.rs`, `src/satterthwaite.rs`, `src/kenward_roger.rs`, `src/kr_modcomp.rs`; named modules such as `predict.rs`, `simulate.rs`, `emmeans.rs`, `mcp.rs`, `robust.rs` |
28
+ | Python API and tests | `python/src/lib.rs`, `python/tests/` |
29
+ | Regression tests and reference evidence | `tests/`, `tests/data/`, `comparisons/` |
30
+ | Checks, aliases, hooks, tool versions | [scripts/ci/lme_ci.py](scripts/ci/lme_ci.py), [Taskfile.yml](Taskfile.yml), [lefthook.yml](lefthook.yml), [mise.toml](mise.toml) |
31
+
32
+ ## Required validation
33
+
34
+ Use the smallest tier covering the change. Requirements from multiple rows combine;
35
+ a successful broader check can satisfy its component checks.
36
+
37
+ | Change | Minimum checks and escalation |
38
+ |---|---|
39
+ | Rust code | `task lint`, `task test:fast`, and affected integration tests; use `task rust` for cross-module or public-API changes |
40
+ | Python bindings | `task lint:python`, `task python` |
41
+ | CI, manifests, release tooling | `task preflight`; use `task ci` before a release or broad refactor |
42
+ | R / Julia comparison scripts | `task lint:comparisons`; use `task lint:comparisons:required` when formatters are installed |
43
+ | Documentation (including this file) or portable examples | `task docs:check`; add `task consumer:smoke` when install or example behavior changes |
44
+ | LMM throughput paths (`src/math.rs`, `src/optimizer.rs`, related caches/solvers) | Read [OPTIMIZATION.md](OPTIMIZATION.md) and run applicable fair-harness cases in addition to Rust checks |
45
+ | `README.md`, `REPO_COMPLETION_BY_AREA.md`, `completion_manifest.json`, `scripts/ci/check_completion_score.py` | `task completion:check` |
46
+
47
+ ### Avoid redundant work
48
+
49
+ - Start with `cargo test --locked --test <test_target> <test_filter>` or
50
+ `cargo test --locked --lib <test_filter>`. Selecting zero tests proves nothing.
51
+ - `task rust` includes Rust lint, all-target compilation, unit/integration tests,
52
+ doctests, and documentation generation. Add `task lint:python` for the Ruff part
53
+ of `task lint`; do not separately repeat `task test:fast`.
54
+ - `task test` runs the full Rust suite. On Windows/macOS, `task test:consolidated`
55
+ runs unit and integration tests with one integration executable, checks examples,
56
+ and runs doctests. It can replace `task test` when linking is costly. An equivalent
57
+ Rust validation slice is `task lint:rust`, `task check`, `task test:consolidated`,
58
+ and `python scripts/ci/lme_ci.py doc`.
59
+ - `task ci` includes Rust, bindings, portable examples, lint, compilation, legal,
60
+ documentation, and completion checks. It does not replace audits/metadata in
61
+ `task preflight`, R/Julia checks, or performance harnesses. `task ci:fast` skips
62
+ the isolated-wheel pass; report that limitation.
63
+ - Reuse passing results for unchanged files/dependencies in the same task. Rerun
64
+ affected checks after edits; broaden when scope or evidence requires it.
65
+ Keep hooks enabled even when they repeat earlier checks.
66
+ - Serialize Cargo commands sharing a target directory. Capture verbose logs and
67
+ inspect exit status and summaries. A timeout or compilation in progress is not a failed test.
68
+
69
+ ## Correctness and evidence
70
+
71
+ - Add regression coverage for bug fixes. Prefer statistical identities, explicit
72
+ expected behavior, and independent fixtures over duplicating the implementation.
73
+ - Register new integration test files in [tests/ci_consolidated.rs](tests/ci_consolidated.rs).
74
+ - For R/Julia parity, match rows, formula, family/link, weights, offsets, and ML/REML.
75
+ Explain tolerances; do not loosen them just to make a failure pass.
76
+ - Tie benchmark claims to the revision, environment, and measured cases. Follow
77
+ [BENCHMARKS.md](BENCHMARKS.md) and [OPTIMIZATION.md](OPTIMIZATION.md); a focused
78
+ speedup does not establish general parity or completion.
79
+
80
+ ### Completion scores are generated claims
81
+
82
+ [completion_manifest.json](completion_manifest.json) governs the README headline
83
+ and all percentages in [REPO_COMPLETION_BY_AREA.md](REPO_COMPLETION_BY_AREA.md).
84
+ It declares weighted binary commitments, locked `scope` strings, evidence paths,
85
+ and a `gap` for every incomplete criterion.
86
+
87
+ - Complete an item only when its locked scope is met and evidence is current.
88
+ Partial, stale, or substituted evidence earns zero; an API or focused benchmark is insufficient.
89
+ - Do not narrow/replace `scope` to earn credit or edit generated percentages by hand.
90
+ Update the manifest and supporting evidence together.
91
+ - `task completion:check` validates schema version 2, names/scopes/gaps, evidence
92
+ paths, arithmetic, report rows, and the README headline. `task ci` includes it.
93
+
94
+ ## Setup and recovery
95
+
96
+ - [mise.toml](mise.toml) configures Rust stable, Python 3.11, uv, Task, and Lefthook.
97
+ Use `task setup` for tools and hooks; run `mise install` first if Task is missing.
98
+ With tools already installed, use `task hooks:install`. Pre-push audits also
99
+ require `cargo-audit` (`cargo install cargo-audit`).
100
+ - For missing PATH entries, use `mise exec -- task <name>`. Locate installed tools
101
+ before reinstalling. Verify subprocess discovery in the actual environment with
102
+ `python -c "import shutil; print(shutil.which('uv'))"` when necessary.
103
+ - Without Task, use `python scripts/ci/lme_ci.py <subcommand>` (`python3` where
104
+ appropriate). Mappings are in [Taskfile.yml](Taskfile.yml): `test:fast` → `test-fast`,
105
+ `rust` → `rust-all`, `lint:python` → `ruff-lint`, `docs:check` → `docs-check`.
106
+ Use `--help` for all commands. Implement new checks in this runner, not duplicate shell logic.
107
+ - For Git dubious ownership, use `git -c safe.directory=C:/path/to/lme-rs <command>`
108
+ with this checkout's absolute forward-slash path; do not trust all directories globally.
109
+ - x86_64 builds link static Intel MKL via `ndarray-linalg`; first builds can be slow.
110
+ Run numerical examples with `--release`, e.g. `cargo run --release --locked --example sleepstudy`.
111
+ Leave the CI runner's own build profiles unchanged.
112
+ - Windows linker errors `LNK1318`/`LNK1106` can indicate disk exhaustion. Check space
113
+ and active builds first. Before cleaning, verify the target belongs to this checkout
114
+ and no build uses it; avoid broad cache deletion.
115
+ - `uv sync` can uninstall the editable Python extension. Restore it from `python/`
116
+ with `uv run --no-sync maturin develop --release`, or rerun `task python`.
117
+ Run Python examples there with `uv run --no-sync python examples/<name>.py`.
118
+
119
+ ## Git, hooks, and release boundaries
120
+
121
+ - Follow the user's commit/push scope; commit authorization alone does not authorize
122
+ pushing. When already authorized, complete validation and delivery without asking again.
123
+ - For requested updates, fetch before comparing branches; fast-forward only when
124
+ ancestry and local changes permit it. After pushing, verify HEAD, the tracking ref,
125
+ and live remote branch SHA agree, and report remaining local changes.
126
+ - Lefthook checks matching staged files: Rust format/Clippy, Python Ruff, comparison
127
+ formatting, manifests, benchmarks/dashboard, and metadata. Hooks may auto-stage
128
+ formatting; inspect the result. They do not run full Rust/bindings suites or `pip-audit`.
129
+ - Pre-push runs `task preflight`: lint, all-target compilation, Cargo audits,
130
+ legal/provenance, and metadata validation. Use `--no-verify` only when explicitly
131
+ necessary; report the bypass and omitted checks.
132
+ - Hosted validation runs on PRs, `v*` tags, and manual dispatch, not ordinary branch
133
+ pushes. Cache-prime is not validation. Hosted coverage adds the OS/Python matrix,
134
+ production-load gates, and `pip-audit`; PRs skip the four heavy ignored cases.
135
+ - Inspect the exact failing CI job/log before changing code. Do not merge with a
136
+ failing security audit. For metadata authentication, use `REPO_ADMIN_TOKEN` with
137
+ `task repo-metadata`; a hosted `401` requires checking/rotating the Actions secret.
138
+ - Preserve full-SHA action pins and readable version comments. After BLAS target-table
139
+ or release-workflow changes, run `task ci` or manually dispatch CI before tagging;
140
+ Windows/Linux cannot validate macOS Apple Silicon BLAS.
141
+ - Tag CI gates crates.io publishing and dispatches PyPI only after validation succeeds.
142
+ Keep PyPI top-level for Trusted Publishing attestations; publishing workflows must
143
+ not independently listen to tags. Read [RELEASING.md](RELEASING.md) before releases.
144
+ - R/Julia benchmark workflow coverage belongs in tag/manual runs. `task benchmarks:preflight`
145
+ includes Rust smoke and R smoke when R/lme4 is installed. Optional tool skips are not passes.
146
+
147
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for extended guidance and
148
+ [Taskfile.yml](Taskfile.yml) for less common commands.
@@ -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 **&lt;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.