lme-python 0.2.1__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 (388) hide show
  1. lme_python-0.2.3/.github/FUNDING.yml +1 -0
  2. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/audit.yml +2 -2
  3. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/benchmarks.yml +76 -0
  4. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/cache-prime.yml +9 -3
  5. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/ci.yml +30 -15
  6. {lme_python-0.2.1 → 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.1 → lme_python-0.2.3}/.github/workflows/python-release.yml +89 -9
  9. {lme_python-0.2.1 → lme_python-0.2.3}/AGENTS.md +19 -6
  10. {lme_python-0.2.1 → lme_python-0.2.3}/BENCHMARKS.md +74 -30
  11. {lme_python-0.2.1 → lme_python-0.2.3}/BENCHMARK_COVERAGE.md +35 -11
  12. {lme_python-0.2.1 → lme_python-0.2.3}/CHANGELOG.md +78 -1
  13. {lme_python-0.2.1 → 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.1 → lme_python-0.2.3}/Cargo.lock +1 -1
  16. {lme_python-0.2.1 → lme_python-0.2.3}/Cargo.toml +1 -1
  17. {lme_python-0.2.1 → lme_python-0.2.3}/GUIDE.md +138 -51
  18. {lme_python-0.2.1 → lme_python-0.2.3}/OPTIMIZATION.md +90 -6
  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.1 → lme_python-0.2.3}/Taskfile.yml +30 -0
  24. lme_python-0.2.3/USABILITY.md +110 -0
  25. {lme_python-0.2.1 → lme_python-0.2.3}/benches/bench_math.rs +21 -0
  26. lme_python-0.2.3/benchmarks/external-nlmm-inference-python-timings-2026-08-14.json +198 -0
  27. lme_python-0.2.3/benchmarks/fair-rust-julia-reference-2026-09-09-refactor.json +1693 -0
  28. lme_python-0.2.3/benchmarks/refactor-2026-09-09.json +1854 -0
  29. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/COMPARISONS.md +34 -8
  30. lme_python-0.2.3/comparisons/bench_external_timings.R +138 -0
  31. lme_python-0.2.3/comparisons/pastes_emmeans.R +48 -0
  32. lme_python-0.2.3/comparisons/pastes_glht.R +36 -0
  33. lme_python-0.2.3/comparisons/sleepstudy_confint_profile.R +16 -0
  34. lme_python-0.2.3/completion_manifest.json +159 -0
  35. lme_python-0.2.3/docs/CALO_CALIBRATION.md +98 -0
  36. lme_python-0.2.3/docs/EXAMPLES.md +86 -0
  37. lme_python-0.2.3/docs/README.md +69 -0
  38. lme_python-0.2.3/docs/TROUBLESHOOTING.md +91 -0
  39. lme_python-0.2.3/docs/benchmarks/app.js +397 -0
  40. lme_python-0.2.3/docs/benchmarks/data/latest.json +834 -0
  41. lme_python-0.2.3/docs/benchmarks/index.html +133 -0
  42. {lme_python-0.2.1 → lme_python-0.2.3}/docs/benchmarks/styles.css +62 -3
  43. {lme_python-0.2.1 → lme_python-0.2.3}/docs/index.html +1 -1
  44. lme_python-0.2.3/examples/bench_external_timings.rs +262 -0
  45. lme_python-0.2.3/examples/explore_formula_ast.rs +137 -0
  46. lme_python-0.2.3/examples/explore_mcp_adjust.rs +78 -0
  47. lme_python-0.2.3/examples/explore_theta_grid.rs +76 -0
  48. {lme_python-0.2.1 → lme_python-0.2.3}/lefthook.yml +4 -0
  49. lme_python-0.2.3/lme-rs-icon.png +0 -0
  50. lme_python-0.2.3/lme-rs.png +0 -0
  51. {lme_python-0.2.1 → lme_python-0.2.3}/pyproject.toml +8 -0
  52. {lme_python-0.2.1 → lme_python-0.2.3}/python/Cargo.lock +2 -2
  53. {lme_python-0.2.1 → lme_python-0.2.3}/python/Cargo.toml +2 -1
  54. {lme_python-0.2.1 → lme_python-0.2.3}/python/PYTHON_GUIDE.md +195 -114
  55. lme_python-0.2.3/python/README.md +104 -0
  56. lme_python-0.2.3/python/examples/plotting_demo/README.md +101 -0
  57. lme_python-0.2.3/python/examples/verification_project/README.md +51 -0
  58. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/verification_project/parity.py +5 -3
  59. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/verification_project/run.py +1 -1
  60. {lme_python-0.2.1 → lme_python-0.2.3}/python/lme_python.pyi +114 -12
  61. lme_python-0.2.3/python/src/controls.rs +61 -0
  62. lme_python-0.2.3/python/src/fitting.rs +597 -0
  63. lme_python-0.2.3/python/src/input.rs +107 -0
  64. {lme_python-0.2.1 → lme_python-0.2.3}/python/src/lib.rs +579 -758
  65. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_api_parity.py +59 -9
  66. lme_python-0.2.3/python/tests/test_model_contracts.py +133 -0
  67. {lme_python-0.2.1 → lme_python-0.2.3}/python/uv.lock +4 -4
  68. lme_python-0.2.3/scripts/build_benchmark_site.py +508 -0
  69. lme_python-0.2.3/scripts/ci/README.md +55 -0
  70. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/check_completion_score.py +36 -3
  71. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/lme_ci.py +283 -3
  72. lme_python-0.2.3/scripts/explorations/README.md +24 -0
  73. lme_python-0.2.3/scripts/run_external_timings.py +236 -0
  74. lme_python-0.2.3/src/basis.rs +529 -0
  75. {lme_python-0.2.1 → lme_python-0.2.3}/src/bootstrap.rs +249 -119
  76. {lme_python-0.2.1 → lme_python-0.2.3}/src/contrast.rs +52 -34
  77. {lme_python-0.2.1 → lme_python-0.2.3}/src/cv.rs +31 -85
  78. lme_python-0.2.3/src/display.rs +283 -0
  79. lme_python-0.2.3/src/emmeans.rs +459 -0
  80. lme_python-0.2.3/src/execution.rs +48 -0
  81. {lme_python-0.2.1 → lme_python-0.2.3}/src/family.rs +65 -0
  82. lme_python-0.2.3/src/formula.rs +1361 -0
  83. {lme_python-0.2.1 → lme_python-0.2.3}/src/glmm_math.rs +226 -82
  84. {lme_python-0.2.1 → lme_python-0.2.3}/src/intercept_blocked.rs +19 -0
  85. lme_python-0.2.3/src/kenward_roger.rs +81 -0
  86. {lme_python-0.2.1 → lme_python-0.2.3}/src/kr_modcomp.rs +24 -45
  87. {lme_python-0.2.1 → lme_python-0.2.3}/src/kr_vcov_adj.rs +7 -12
  88. {lme_python-0.2.1 → lme_python-0.2.3}/src/lib.rs +254 -576
  89. {lme_python-0.2.1 → lme_python-0.2.3}/src/math.rs +99 -84
  90. lme_python-0.2.3/src/mcp.rs +350 -0
  91. lme_python-0.2.3/src/model.rs +240 -0
  92. {lme_python-0.2.1 → lme_python-0.2.3}/src/model_matrix.rs +586 -228
  93. lme_python-0.2.3/src/nlmm/block_solve.rs +132 -0
  94. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/fit.rs +386 -146
  95. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/mod.rs +1 -0
  96. lme_python-0.2.3/src/ols.rs +33 -0
  97. {lme_python-0.2.1 → lme_python-0.2.3}/src/optimizer.rs +252 -91
  98. lme_python-0.2.3/src/predict.rs +179 -0
  99. lme_python-0.2.3/src/prepared.rs +76 -0
  100. lme_python-0.2.3/src/profile_ci.rs +1113 -0
  101. {lme_python-0.2.1 → lme_python-0.2.3}/src/robust.rs +18 -2
  102. {lme_python-0.2.1 → lme_python-0.2.3}/src/satterthwaite.rs +3 -9
  103. {lme_python-0.2.1 → lme_python-0.2.3}/src/simulate.rs +18 -17
  104. lme_python-0.2.3/src/studentized_range.rs +224 -0
  105. {lme_python-0.2.1 → lme_python-0.2.3}/tests/ci_consolidated.rs +19 -0
  106. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_gamma_dyestuff.json +4 -1
  107. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/golden_parity_manifest.json +8 -1
  108. lme_python-0.2.3/tests/data/pastes_emmeans.json +15 -0
  109. lme_python-0.2.3/tests/data/pastes_glht_tukey.json +18 -0
  110. lme_python-0.2.3/tests/data/r_edge_case_matrix.json +432 -0
  111. lme_python-0.2.3/tests/data/sleepstudy_confint_profile_vc.json +12 -0
  112. {lme_python-0.2.1 → lme_python-0.2.3}/tests/generate_nlmm_fixtures.R +17 -6
  113. lme_python-0.2.3/tests/generate_r_edge_case_matrix.R +211 -0
  114. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_anova.rs +2 -0
  115. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_bootstrap.rs +15 -0
  116. lme_python-0.2.3/tests/test_bug_hunt.rs +353 -0
  117. lme_python-0.2.3/tests/test_bug_hunt_more.rs +212 -0
  118. lme_python-0.2.3/tests/test_bug_hunt_numerics.rs +287 -0
  119. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_confint_profile.rs +80 -1
  120. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_coverage_gaps.rs +3 -1
  121. lme_python-0.2.3/tests/test_emmeans.rs +166 -0
  122. lme_python-0.2.3/tests/test_explorations.rs +159 -0
  123. lme_python-0.2.3/tests/test_external_timings_artifact.rs +72 -0
  124. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_failure_modes.rs +14 -14
  125. lme_python-0.2.3/tests/test_formula.rs +94 -0
  126. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_formula_stress.rs +171 -11
  127. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_glmm.rs +86 -1
  128. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_glmm_offset_grouseticks.rs +11 -1
  129. lme_python-0.2.3/tests/test_mcp.rs +182 -0
  130. lme_python-0.2.3/tests/test_model_contracts.rs +333 -0
  131. lme_python-0.2.3/tests/test_nlmm_agq.rs +75 -0
  132. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_predict.rs +4 -3
  133. lme_python-0.2.3/tests/test_r_edge_case_matrix.rs +247 -0
  134. lme_python-0.2.1/CONTRIBUTING.md +0 -217
  135. lme_python-0.2.1/PKG-INFO +0 -15
  136. lme_python-0.2.1/README.md +0 -113
  137. lme_python-0.2.1/RELEASING.md +0 -260
  138. lme_python-0.2.1/REPO_COMPLETION_BY_AREA.md +0 -110
  139. lme_python-0.2.1/USABILITY.md +0 -185
  140. lme_python-0.2.1/completion_manifest.json +0 -20
  141. lme_python-0.2.1/docs/CALO_CALIBRATION.md +0 -124
  142. lme_python-0.2.1/docs/benchmarks/app.js +0 -207
  143. lme_python-0.2.1/docs/benchmarks/index.html +0 -92
  144. lme_python-0.2.1/python/examples/plotting_demo/README.md +0 -129
  145. lme_python-0.2.1/python/examples/verification_project/README.md +0 -42
  146. lme_python-0.2.1/scripts/build_benchmark_site.py +0 -210
  147. lme_python-0.2.1/scripts/ci/README.md +0 -27
  148. lme_python-0.2.1/src/formula.rs +0 -653
  149. lme_python-0.2.1/src/kenward_roger.rs +0 -357
  150. lme_python-0.2.1/src/profile_ci.rs +0 -473
  151. lme_python-0.2.1/tests/test_formula.rs +0 -19
  152. lme_python-0.2.1/tests/test_nlmm_agq.rs +0 -37
  153. {lme_python-0.2.1 → lme_python-0.2.3}/.JuliaFormatter.toml +0 -0
  154. {lme_python-0.2.1 → lme_python-0.2.3}/.github/dependabot.yml +0 -0
  155. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/crate-publish-dry-run.yml +0 -0
  156. {lme_python-0.2.1 → lme_python-0.2.3}/.github/workflows/repo-metadata.yml +0 -0
  157. {lme_python-0.2.1 → lme_python-0.2.3}/.gitignore +0 -0
  158. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSE +0 -0
  159. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSES/Apache-2.0.txt +0 -0
  160. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSES/BSD-3-Clause-OpenBLAS.txt +0 -0
  161. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSES/GPL-2.0-or-later.txt +0 -0
  162. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSES/Intel-Simplified-Software-License.txt +0 -0
  163. {lme_python-0.2.1 → lme_python-0.2.3}/LICENSES/LGPL-2.1-only.txt +0 -0
  164. {lme_python-0.2.1 → lme_python-0.2.3}/RELINKING.md +0 -0
  165. {lme_python-0.2.1 → lme_python-0.2.3}/THIRD_PARTY_NOTICES.md +0 -0
  166. {lme_python-0.2.1 → lme_python-0.2.3}/benches/bench_load_production.rs +0 -0
  167. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-04.json +0 -0
  168. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-06.json +0 -0
  169. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-08.json +0 -0
  170. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-direct-intercept-gram.json +0 -0
  171. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-full-lmm.json +0 -0
  172. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-glmm.json +0 -0
  173. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-intercept-setup.json +0 -0
  174. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes-linear-cache.json +0 -0
  175. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes.json +0 -0
  176. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09-sleepstudy-slopes.json +0 -0
  177. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-09.json +0 -0
  178. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-10-cold-setup-followup.json +0 -0
  179. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-16-cold-fit-lt1.json +0 -0
  180. {lme_python-0.2.1 → lme_python-0.2.3}/benchmarks/fair-rust-julia-reference-2026-07-22-full-tier-a.json +0 -0
  181. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/bench_fair_julia_perf.jl +0 -0
  182. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/bench_fair_julia_timing.jl +0 -0
  183. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/bench_fair_rust_julia.rs +0 -0
  184. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/bench_perf_breakdown.rs +0 -0
  185. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/categorical_anova.R +0 -0
  186. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/categorical_anova.jl +0 -0
  187. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/categorical_anova.py +0 -0
  188. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/categorical_anova.rs +0 -0
  189. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/comparison_chart.png +0 -0
  190. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_cbpp.R +0 -0
  191. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_cbpp.jl +0 -0
  192. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_cbpp.py +0 -0
  193. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_cbpp.rs +0 -0
  194. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_gaussian.R +0 -0
  195. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_gaussian.jl +0 -0
  196. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_gaussian.py +0 -0
  197. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_gaussian.rs +0 -0
  198. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_grouseticks.R +0 -0
  199. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_grouseticks.jl +0 -0
  200. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_grouseticks.py +0 -0
  201. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/glmm_grouseticks.rs +0 -0
  202. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmer_weighted.R +0 -0
  203. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmer_weighted.jl +0 -0
  204. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmer_weighted.py +0 -0
  205. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmer_weighted.rs +0 -0
  206. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_dyestuff.R +0 -0
  207. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_dyestuff.jl +0 -0
  208. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_dyestuff.py +0 -0
  209. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_dyestuff.rs +0 -0
  210. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_pastes.R +0 -0
  211. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_pastes.jl +0 -0
  212. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_pastes.py +0 -0
  213. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_pastes.rs +0 -0
  214. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_penicillin.R +0 -0
  215. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_penicillin.jl +0 -0
  216. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_penicillin.py +0 -0
  217. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/lmm_penicillin.rs +0 -0
  218. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/nlmm_sspower.R +0 -0
  219. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_offset.R +0 -0
  220. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_offset.jl +0 -0
  221. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_probit.R +0 -0
  222. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_probit.jl +0 -0
  223. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_weighted.R +0 -0
  224. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/glmm_weighted.jl +0 -0
  225. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/parity_r_setup.R +0 -0
  226. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/sleepstudy_offset.R +0 -0
  227. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity/sleepstudy_offset.jl +0 -0
  228. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/parity_export.rs +0 -0
  229. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/r_setup.R +0 -0
  230. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy.R +0 -0
  231. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy.jl +0 -0
  232. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy.py +0 -0
  233. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy.rs +0 -0
  234. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy_ml.R +0 -0
  235. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy_ml.jl +0 -0
  236. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy_ml.py +0 -0
  237. {lme_python-0.2.1 → lme_python-0.2.3}/comparisons/sleepstudy_ml.rs +0 -0
  238. {lme_python-0.2.1 → lme_python-0.2.3}/examples/batch_sspower_cpu.rs +0 -0
  239. {lme_python-0.2.1 → lme_python-0.2.3}/examples/dump_cascade_fixtures.rs +0 -0
  240. {lme_python-0.2.1 → lme_python-0.2.3}/examples/nlmm_fixture_dump.rs +0 -0
  241. {lme_python-0.2.1 → lme_python-0.2.3}/legal/dependency-license-exceptions.json +0 -0
  242. {lme_python-0.2.1 → lme_python-0.2.3}/legal/fixture-provenance.json +0 -0
  243. {lme_python-0.2.1 → lme_python-0.2.3}/mise.toml +0 -0
  244. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/glmer_cbpp.py +0 -0
  245. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/glmer_grouseticks.py +0 -0
  246. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/lm_basics.py +0 -0
  247. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/lmer_sleepstudy.py +0 -0
  248. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/model_comparison.py +0 -0
  249. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/.gitignore +0 -0
  250. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/compare_plots.py +0 -0
  251. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/figure_specs.py +0 -0
  252. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/find_rscript.py +0 -0
  253. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/numeric_overlay.py +0 -0
  254. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/paths.py +0 -0
  255. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/plot_demo.py +0 -0
  256. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/plot_r.R +0 -0
  257. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/plotting_demo/run_all.py +0 -0
  258. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/verification_project/conftest.py +0 -0
  259. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/verification_project/paths.py +0 -0
  260. {lme_python-0.2.1 → lme_python-0.2.3}/python/examples/verification_project/test_parity.py +0 -0
  261. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_basic.py +0 -0
  262. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_cv.py +0 -0
  263. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_dataframe_inputs.py +0 -0
  264. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_golden_parity.py +0 -0
  265. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_nlmer_custom_mean.py +0 -0
  266. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_simulate_scale.py +0 -0
  267. {lme_python-0.2.1 → lme_python-0.2.3}/python/tests/test_wheel_notices.py +0 -0
  268. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/check_legal_compliance.py +0 -0
  269. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/julia_format.jl +0 -0
  270. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/package_wheel_notices.py +0 -0
  271. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/r_format.R +0 -0
  272. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/ci/run.sh +0 -0
  273. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/dump_dyestuff.R +0 -0
  274. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/dump_pastes.R +0 -0
  275. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/local_ci.ps1 +0 -0
  276. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/local_ci.sh +0 -0
  277. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/make_penicillin_csv.py +0 -0
  278. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/plot_comparisons.py +0 -0
  279. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/run_cross_language_benchmarks.py +0 -0
  280. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/run_fair_rust_julia_benchmark.py +0 -0
  281. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/run_perf_breakdown.py +0 -0
  282. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/sync_github_repo_metadata.py +0 -0
  283. {lme_python-0.2.1 → lme_python-0.2.3}/scripts/verify_cross_language_parity.py +0 -0
  284. {lme_python-0.2.1 → lme_python-0.2.3}/src/anova.rs +0 -0
  285. {lme_python-0.2.1 → lme_python-0.2.3}/src/anova_contrasts.rs +0 -0
  286. {lme_python-0.2.1 → lme_python-0.2.3}/src/ddf.rs +0 -0
  287. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/formula.rs +0 -0
  288. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/mean.rs +0 -0
  289. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/mean_fn.rs +0 -0
  290. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/predict.rs +0 -0
  291. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/re_cov.rs +0 -0
  292. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/self_start.rs +0 -0
  293. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssasymp.rs +0 -0
  294. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssasympoff.rs +0 -0
  295. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssasymporig.rs +0 -0
  296. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssbiexp.rs +0 -0
  297. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssfpl.rs +0 -0
  298. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssgompertz.rs +0 -0
  299. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/sslogis.rs +0 -0
  300. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssmicmen.rs +0 -0
  301. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/sspower.rs +0 -0
  302. {lme_python-0.2.1 → lme_python-0.2.3}/src/nlmm/ssweibull.rs +0 -0
  303. {lme_python-0.2.1 → lme_python-0.2.3}/src/perf_diag.rs +0 -0
  304. {lme_python-0.2.1 → lme_python-0.2.3}/src/quadrature.rs +0 -0
  305. {lme_python-0.2.1 → lme_python-0.2.3}/tests/categorical_anova_test.rs +0 -0
  306. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/cbpp_binary.csv +0 -0
  307. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/cbpp_binary_weighted.csv +0 -0
  308. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/dyestuff.csv +0 -0
  309. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/dyestuff_intercept_reml.json +0 -0
  310. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_binomial.json +0 -0
  311. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_binomial_agq.json +0 -0
  312. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_binomial_cloglog.json +0 -0
  313. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_binomial_probit.json +0 -0
  314. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_binomial_weighted.json +0 -0
  315. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_poisson.json +0 -0
  316. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/glmm_poisson_offset.json +0 -0
  317. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/grouseticks.csv +0 -0
  318. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/intercept_only.json +0 -0
  319. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/mock_crossed.json +0 -0
  320. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/mock_ml.json +0 -0
  321. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/orange.csv +0 -0
  322. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/orange_nlmer.json +0 -0
  323. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/pastes.csv +0 -0
  324. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/pastes_cask_reml.json +0 -0
  325. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/penicillin.csv +0 -0
  326. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/penicillin.json +0 -0
  327. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/random_slopes.json +0 -0
  328. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/sleepstudy.csv +0 -0
  329. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/sleepstudy_confint_profile.json +0 -0
  330. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/sleepstudy_offset_reml.json +0 -0
  331. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssasymp_synthetic.csv +0 -0
  332. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssasympoff_nlmer.json +0 -0
  333. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssasympoff_synthetic.csv +0 -0
  334. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssasymporig_nlmer.json +0 -0
  335. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssasymporig_synthetic.csv +0 -0
  336. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssbiexp_nlmer.json +0 -0
  337. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssbiexp_synthetic.csv +0 -0
  338. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssfol_nlmer.json +0 -0
  339. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssfpl_nlmer.json +0 -0
  340. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssfpl_synthetic.csv +0 -0
  341. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssgompertz_nlmer.json +0 -0
  342. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssgompertz_synthetic.csv +0 -0
  343. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssmicmen_nlmer.json +0 -0
  344. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssmicmen_synthetic.csv +0 -0
  345. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/sspower_nlmer.json +0 -0
  346. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/sspower_synthetic.csv +0 -0
  347. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssweibull_nlmer.json +0 -0
  348. {lme_python-0.2.1 → lme_python-0.2.3}/tests/data/ssweibull_synthetic.csv +0 -0
  349. {lme_python-0.2.1 → lme_python-0.2.3}/tests/gen_nlmm_extras.R +0 -0
  350. {lme_python-0.2.1 → lme_python-0.2.3}/tests/generate_mock_data.py +0 -0
  351. {lme_python-0.2.1 → lme_python-0.2.3}/tests/generate_test_data.R +0 -0
  352. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_anova_types.rs +0 -0
  353. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_conditional_real.rs +0 -0
  354. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_confint_simulate.rs +0 -0
  355. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_contrast.rs +0 -0
  356. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_coverage_edge_cases.rs +0 -0
  357. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_crossed_mock.rs +0 -0
  358. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_cv.rs +0 -0
  359. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_e2e_lmer.rs +0 -0
  360. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_edge_cases.rs +0 -0
  361. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_errors.rs +0 -0
  362. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_gaps.rs +0 -0
  363. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_glmm_links.rs +0 -0
  364. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_glmm_weighted.rs +0 -0
  365. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_golden_parity.rs +0 -0
  366. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_intercept_only.rs +0 -0
  367. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_kenward_roger.rs +0 -0
  368. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_kr_modcomp_pastes.rs +0 -0
  369. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_ml_optimization.rs +0 -0
  370. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_bounds.rs +0 -0
  371. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_custom_mean.rs +0 -0
  372. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_orange.rs +0 -0
  373. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_orange_multi_re.rs +0 -0
  374. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_self_start.rs +0 -0
  375. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_ss_new_means.rs +0 -0
  376. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_ssasymp.rs +0 -0
  377. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_ssasymp_off_orig.rs +0 -0
  378. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_ssfol.rs +0 -0
  379. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_ssmicmen.rs +0 -0
  380. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_nlmm_sspower.rs +0 -0
  381. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_no_intercept.rs +0 -0
  382. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_numerical_parity.rs +0 -0
  383. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_production_load.rs +0 -0
  384. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_random_slopes.rs +0 -0
  385. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_robust.rs +0 -0
  386. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_sandwich_math.R +0 -0
  387. {lme_python-0.2.1 → lme_python-0.2.3}/tests/test_satterthwaite.rs +0 -0
  388. {lme_python-0.2.1 → 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@c44f6b046f1c29ae5918b1e0bfdbb2f1813836fd # 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@e18b497796c12c097a38f9edb9d0641fb99eee32 # 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@e18b497796c12c097a38f9edb9d0641fb99eee32 # 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,11 +105,17 @@ 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@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
108
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
109
109
  with:
110
110
  shared-key: ci-${{ runner.os }}-python
111
111
  workspaces: python -> target
112
112
  - name: Compile Python binding dependencies
113
+ if: matrix.os != 'macos-latest'
113
114
  env:
114
115
  PYO3_PYTHON: python
115
116
  run: cargo build --release --manifest-path python/Cargo.toml
117
+ - name: Compile Python binding dependencies (macOS)
118
+ if: matrix.os == 'macos-latest'
119
+ uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1
120
+ with:
121
+ args: --release --out python/dist --manifest-path python/Cargo.toml
@@ -34,6 +34,10 @@ jobs:
34
34
  python-version: "3.11"
35
35
  - name: Validate legal notices and provenance
36
36
  run: python3 scripts/ci/lme_ci.py legal
37
+ - name: Validate Markdown links
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
37
41
  - name: Validate completion score claims
38
42
  run: python3 scripts/ci/lme_ci.py completion-check
39
43
 
@@ -54,7 +58,7 @@ jobs:
54
58
  - name: Install OpenBLAS
55
59
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
56
60
  - name: Cache Rust dependencies
57
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
61
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
58
62
  with:
59
63
  shared-key: ci-v2-${{ runner.os }}-rust-test
60
64
  save-if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/master' }}
@@ -111,7 +115,7 @@ jobs:
111
115
  toolchain: stable
112
116
 
113
117
  - name: Cache Rust dependencies
114
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
118
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
115
119
  with:
116
120
  shared-key: ci-v2-${{ runner.os }}-rust-test
117
121
  cache-workspace-crates: true
@@ -139,7 +143,7 @@ jobs:
139
143
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
140
144
 
141
145
  - name: Restore Rust dependencies
142
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
146
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
143
147
  with:
144
148
  shared-key: ci-v2-${{ runner.os }}-rust-release
145
149
  cache-workspace-crates: true
@@ -167,13 +171,13 @@ jobs:
167
171
  - name: Install OpenBLAS
168
172
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
169
173
  - name: Cache Python Rust dependencies
170
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
174
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
171
175
  with:
172
176
  shared-key: ci-${{ runner.os }}-python
173
177
  workspaces: python -> target
174
178
  save-if: ${{ matrix.python-version == '3.11' && (github.event_name == 'pull_request' || github.ref == 'refs/heads/master') }}
175
179
  - name: Set up uv
176
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
180
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
177
181
  with:
178
182
  python-version: ${{ matrix.python-version }}
179
183
  enable-cache: true
@@ -181,7 +185,7 @@ jobs:
181
185
 
182
186
  - name: Python bindings (canonical editable identity + isolated wheel)
183
187
  if: matrix.python-version == '3.11'
184
- run: python scripts/ci/lme_ci.py python --python-version ${{ matrix.python-version }}
188
+ run: python scripts/ci/lme_ci.py python --python-version ${{ matrix.python-version }} --examples
185
189
 
186
190
  - name: Python bindings (isolated wheel compatibility)
187
191
  if: matrix.python-version != '3.11'
@@ -203,19 +207,19 @@ jobs:
203
207
  if: matrix.os == 'macos-latest'
204
208
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
205
209
  - name: Cache Python Rust dependencies
206
- uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
210
+ uses: Swatinem/rust-cache@f0d9c3887740aee45f6153b24b3a6b815192ec16 # v2
207
211
  with:
208
212
  shared-key: ci-${{ runner.os }}-python
209
213
  workspaces: python -> target
210
214
  save-if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/master' }}
211
215
  - name: Set up uv
212
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
216
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
213
217
  with:
214
218
  python-version: "3.11"
215
219
  enable-cache: true
216
220
  cache-dependency-glob: "python/uv.lock"
217
221
  - name: Python bindings (isolated wheel OS compatibility)
218
- run: python scripts/ci/lme_ci.py python --python-version 3.11 --wheel-only
222
+ run: python scripts/ci/lme_ci.py python --python-version 3.11 --wheel-only --examples
219
223
 
220
224
  publish-crate:
221
225
  name: Publish Rust crate after CI
@@ -236,7 +240,7 @@ jobs:
236
240
  secrets: inherit
237
241
 
238
242
  publish-python:
239
- name: Publish Python artifacts after CI
243
+ name: Dispatch Python release after CI
240
244
  if: startsWith(github.ref, 'refs/tags/v')
241
245
  needs:
242
246
  - release-policy
@@ -247,8 +251,19 @@ jobs:
247
251
  - python-bindings-versions
248
252
  - python-bindings-os
249
253
  permissions:
250
- contents: write
251
- id-token: write
252
- uses: ./.github/workflows/python-release.yml
253
- with:
254
- publish: true
254
+ actions: write
255
+ contents: read
256
+ runs-on: ubuntu-latest
257
+ steps:
258
+ - name: Dispatch verified Python release
259
+ env:
260
+ GH_TOKEN: ${{ github.token }}
261
+ RELEASE_SHA: ${{ github.sha }}
262
+ RELEASE_TAG: ${{ github.ref_name }}
263
+ run: |
264
+ gh workflow run python-release.yml \
265
+ --repo "${GITHUB_REPOSITORY}" \
266
+ --ref master \
267
+ -f release_tag="${RELEASE_TAG}" \
268
+ -f release_sha="${RELEASE_SHA}" \
269
+ -f publish=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@e18b497796c12c097a38f9edb9d0641fb99eee32 # 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
@@ -1,28 +1,96 @@
1
1
  name: Build and Publish Python Wheels
2
2
 
3
- # CI calls this workflow after every release-tag validation job succeeds.
4
- # workflow_dispatch remains available for non-publishing artifact builds.
3
+ # CI dispatches this top-level workflow after every release-tag validation job
4
+ # succeeds. PyPI Trusted Publishing does not support publishing from a reusable
5
+ # workflow with attestations, so this workflow must retain its own identity.
5
6
  on:
6
- workflow_call:
7
+ workflow_dispatch:
7
8
  inputs:
9
+ release_tag:
10
+ description: Validated v* tag to build; blank builds the selected ref
11
+ required: false
12
+ type: string
13
+ default: ""
14
+ release_sha:
15
+ description: Exact validated commit for a publishing run
16
+ required: false
17
+ type: string
18
+ default: ""
8
19
  publish:
9
- description: Publish the built artifacts for a validated release tag
20
+ description: Publish the verified release tag to PyPI
10
21
  required: false
11
22
  type: boolean
12
23
  default: false
13
- workflow_dispatch:
14
24
 
15
25
  permissions:
16
26
  contents: read
17
27
 
28
+ env:
29
+ RELEASE_REF: ${{ inputs.release_tag || github.ref }}
30
+
18
31
  jobs:
32
+ verify-release:
33
+ name: Verify release target
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
37
+ with:
38
+ fetch-depth: 0
39
+ ref: ${{ env.RELEASE_REF }}
40
+
41
+ - name: Verify validated tag and package versions
42
+ if: inputs.publish
43
+ env:
44
+ RELEASE_SHA: ${{ inputs.release_sha }}
45
+ RELEASE_TAG: ${{ inputs.release_tag }}
46
+ run: |
47
+ set -euo pipefail
48
+
49
+ if [[ ! "${RELEASE_TAG}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]; then
50
+ echo "Publishing requires an explicit semantic v* release tag." >&2
51
+ exit 1
52
+ fi
53
+ if [[ ! "${RELEASE_SHA}" =~ ^[0-9a-f]{40}$ ]]; then
54
+ echo "Publishing requires the exact 40-character validated commit SHA." >&2
55
+ exit 1
56
+ fi
57
+
58
+ actual_sha="$(git rev-list -n 1 "refs/tags/${RELEASE_TAG}")"
59
+ if [[ "${actual_sha}" != "${RELEASE_SHA}" ]]; then
60
+ echo "Tag ${RELEASE_TAG} resolves to ${actual_sha}, not validated SHA ${RELEASE_SHA}." >&2
61
+ exit 1
62
+ fi
63
+ if [[ "$(git rev-parse HEAD)" != "${RELEASE_SHA}" ]]; then
64
+ echo "Checked-out release does not match validated SHA ${RELEASE_SHA}." >&2
65
+ exit 1
66
+ fi
67
+
68
+ python - "${RELEASE_TAG#v}" <<'PY'
69
+ import pathlib
70
+ import sys
71
+ import tomllib
72
+
73
+ expected = sys.argv[1]
74
+ manifests = (pathlib.Path("Cargo.toml"), pathlib.Path("python/Cargo.toml"))
75
+ for manifest in manifests:
76
+ with manifest.open("rb") as stream:
77
+ actual = tomllib.load(stream)["package"]["version"]
78
+ if actual != expected:
79
+ raise SystemExit(
80
+ f"{manifest}: package version {actual!r} does not match tag {expected!r}"
81
+ )
82
+ PY
83
+
19
84
  linux:
85
+ needs: verify-release
20
86
  runs-on: ubuntu-latest
21
87
  strategy:
22
88
  matrix:
23
89
  target: [x86_64, aarch64]
24
90
  steps:
25
91
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
92
+ with:
93
+ ref: ${{ env.RELEASE_REF }}
26
94
  - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
27
95
  with:
28
96
  python-version: '3.10'
@@ -59,12 +127,15 @@ jobs:
59
127
  path: dist
60
128
 
61
129
  windows:
130
+ needs: verify-release
62
131
  runs-on: windows-latest
63
132
  strategy:
64
133
  matrix:
65
134
  target: [x64]
66
135
  steps:
67
136
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
137
+ with:
138
+ ref: ${{ env.RELEASE_REF }}
68
139
  - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
69
140
  with:
70
141
  python-version: '3.10'
@@ -89,12 +160,15 @@ jobs:
89
160
  path: dist
90
161
 
91
162
  macos:
163
+ needs: verify-release
92
164
  runs-on: macos-latest
93
165
  strategy:
94
166
  matrix:
95
167
  target: [x86_64, aarch64]
96
168
  steps:
97
169
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
170
+ with:
171
+ ref: ${{ env.RELEASE_REF }}
98
172
  - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
99
173
  with:
100
174
  python-version: '3.10'
@@ -124,9 +198,12 @@ jobs:
124
198
  path: dist
125
199
 
126
200
  sdist:
201
+ needs: verify-release
127
202
  runs-on: ubuntu-latest
128
203
  steps:
129
204
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
205
+ with:
206
+ ref: ${{ env.RELEASE_REF }}
130
207
  - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
131
208
  with:
132
209
  toolchain: stable
@@ -147,14 +224,16 @@ jobs:
147
224
  name: Publish to PyPI
148
225
  needs: [linux, windows, macos, sdist]
149
226
  runs-on: ubuntu-latest
150
- # Only CI's validated release-tag call can enable publication.
151
- if: "inputs.publish && startsWith(github.ref, 'refs/tags/v')"
227
+ # Publication requires the explicit opt-in and exact tag/SHA verification above.
228
+ if: inputs.publish
152
229
  environment: pypi
153
230
  permissions:
154
231
  id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
155
232
  contents: write # needed to create GitHub Release
156
233
  steps:
157
234
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
235
+ with:
236
+ ref: ${{ env.RELEASE_REF }}
158
237
 
159
238
  - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
160
239
  with:
@@ -174,9 +253,10 @@ jobs:
174
253
  done
175
254
 
176
255
  - name: Publish to PyPI
177
- uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # release/v1
256
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
178
257
 
179
258
  - name: Upload to GitHub Release
180
- uses: softprops/action-gh-release@3d0d9888cb7fd7b750713d6e236d1fcb99157228 # v3.0.2
259
+ uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3
181
260
  with:
182
261
  files: dist/*
262
+ tag_name: ${{ inputs.release_tag }}
@@ -10,6 +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` (includes dashboard JSON drift); use `task consumer:smoke` when install or example behavior changes |
13
14
  | LMM throughput paths (`src/math.rs`, `src/optimizer.rs`) | Read [OPTIMIZATION.md](OPTIMIZATION.md) and run the applicable fair-harness cases |
14
15
  | Completion score files: `README.md`, `REPO_COMPLETION_BY_AREA.md`, `completion_manifest.json`, or `scripts/ci/check_completion_score.py` | `task completion:check` |
15
16
 
@@ -19,10 +20,10 @@ For a change that crosses several rows, run every applicable check. Full Rust in
19
20
 
20
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.
21
22
 
22
- - [completion_manifest.json](completion_manifest.json) is the source of truth: it declares binary scope commitments, their weights, and evidence paths.
23
- - `task completion:check` validates evidence-path existence, score arithmetic, report rows, and the README headline.
24
- - A completion item may be marked complete only when its stated evidence is current. Partial or stale evidence earns zero.
25
- - 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.
26
27
 
27
28
  `task ci` runs this check automatically. Run it directly whenever a completion-related file changes.
28
29
 
@@ -70,11 +71,11 @@ Use `--no-verify` only when explicitly necessary; report the bypass and the chec
70
71
 
71
72
  ## CI and release boundaries
72
73
 
73
- GitHub Actions validation runs automatically on pull requests and `v*` tags, and can be manually dispatched. Ordinary non-PR branch pushes do not receive the hosted validation matrix automatically. A lightweight cache-prime workflow runs when Rust dependency inputs change on `master` and weekly so new PRs can reuse trusted dependency artifacts. Pull requests run the full matrix except the four ignored heavy production-load cases. The tag CI calls the crates.io and PyPI workflows only after every validation job succeeds; the publishing workflows do not listen to tags independently.
74
+ GitHub Actions validation runs automatically on pull requests and `v*` tags, and can be manually dispatched. Ordinary non-PR branch pushes do not receive the hosted validation matrix automatically. A lightweight cache-prime workflow runs when Rust dependency inputs change on `master` and weekly so new PRs can reuse trusted dependency artifacts. Pull requests run the full matrix except the four ignored heavy production-load cases. The tag CI calls the crates.io workflow and dispatches the top-level PyPI workflow only after every validation job succeeds; the publishing workflows do not listen to tags independently. The PyPI workflow must remain top-level because Trusted Publishing attestations do not support a reusable publishing workflow.
74
75
 
75
76
  External GitHub Actions are pinned to full commit SHAs, with the readable release line retained as a comment. Dependabot proposes grouped weekly pin updates; do not replace SHA pins with mutable tags. Dependency audits and libFuzzer smoke tests run weekly in addition to their release/manual entry points.
76
77
 
77
- - `task ci` mirrors the core hosted flow: Rust tests, Python bindings, lint, all-targets check, legal checks, doctests, docs, and the completion-score check.
78
+ - `task ci` mirrors the core hosted flow: Rust tests, Python bindings, portable consumer examples, lint, all-targets check, legal checks, documentation/link verification, and the completion-score check.
78
79
  - Hosted-only coverage includes the multi-OS matrix, Python 3.10–3.13, production-load gates, and `pip-audit`.
79
80
  - After changing BLAS target tables or release workflows, run `task ci` locally or manually dispatch CI before tagging; macOS Apple Silicon BLAS is not exercised on Windows/Linux.
80
81
  - Benchmark workflow coverage requiring R or Julia belongs in the tag/manual workflow. `task benchmarks:preflight` runs the Rust smoke plus the R smoke when R/lme4 is available.
@@ -93,8 +94,20 @@ For repository-metadata token issues, set `REPO_ADMIN_TOKEN` locally and run `ta
93
94
  | `task ci` / `task ci:fast` | Core CI mirror / reuse the editable Python environment and skip the isolated-wheel pass |
94
95
  | `task audit` / `task legal` | Security audit / provenance and license checks |
95
96
  | `task completion:check` | Verify the manifest-derived completion score and published markers |
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 |
96
99
  | `task benchmarks:fair-rust-julia` | Fair fit-only Rust vs MixedModels.jl timing when Julia packages are installed |
97
100
  | `task benchmarks:perf-breakdown` | Rust phase timings against Julia optimizer evaluation counts |
101
+ | `task benchmarks:external-timings` | Fair-ish `nlmer`, post-fit inference, and Python FFI timings vs R when available |
102
+ | `task explorations` | Standalone native-parser AST, θ-grid, and MCP exploration examples |
98
103
  | `task lint:comparisons` | Optional R/Julia comparison formatting check |
99
104
 
100
105
  Run `python scripts/ci/lme_ci.py --help` for the complete command list. See [CONTRIBUTING.md](CONTRIBUTING.md) for contributor workflow and [RELEASING.md](RELEASING.md) for release steps.
106
+
107
+ ## Cursor Cloud specific instructions
108
+
109
+ This repo is a pure Rust library (`lme-rs`) plus a PyO3/maturin Python binding (`lme_python`); there are no servers or long-running services. "Running the app" means building the crate and running examples/tests. See the command reference above for the canonical `task` aliases.
110
+
111
+ - Toolchain comes from `mise` (per [mise.toml](mise.toml): Rust stable, Python 3.11, `uv`, `task`, `lefthook`). The startup update script runs `mise install`. Interactive shells auto-activate mise via `~/.bashrc`. In a non-interactive shell where `mise`/`task`/`uv` aren't on `PATH`, run `eval "$($HOME/.local/bin/mise activate bash --shims)"` first (or invoke tools with `$HOME/.local/bin/mise exec -- <cmd>`).
112
+ - On x86_64 the core crate links Intel MKL statically via `ndarray-linalg`, so no `gfortran`/system BLAS is needed; the first `cargo build`/`task test:fast` compiles MKL and takes a few minutes (subsequent builds are cached). Examples must be run with `--release` (e.g. `cargo run --release --example sleepstudy`) — a debug build is very slow.
113
+ - Python bindings gotcha: `uv sync` / `task ci` / `task python` reinstall the venv and **uninstall** the maturin-built `lme_python` before rebuilding it. If you run a bare `uv sync` (or interrupt `task python` before its maturin step), `import lme_python` breaks; restore it with `cd python && uv run --no-sync maturin develop --release` (or re-run `task python`). Run Python examples from `python/` via `uv run --no-sync python examples/<name>.py`.