lme-python 0.1.7__tar.gz → 0.1.9__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 (266) hide show
  1. lme_python-0.1.9/.JuliaFormatter.toml +8 -0
  2. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/audit.yml +10 -28
  3. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/benchmarks.yml +24 -4
  4. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/ci.yml +49 -49
  5. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/python-release.yml +5 -10
  6. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/repo-metadata.yml +13 -6
  7. {lme_python-0.1.7 → lme_python-0.1.9}/.gitignore +1 -0
  8. lme_python-0.1.9/AGENTS.md +192 -0
  9. lme_python-0.1.9/BENCHMARKS.md +386 -0
  10. lme_python-0.1.9/BENCHMARK_COVERAGE.md +106 -0
  11. lme_python-0.1.9/CHANGELOG.md +174 -0
  12. lme_python-0.1.9/CONTRIBUTING.md +191 -0
  13. {lme_python-0.1.7 → lme_python-0.1.9}/Cargo.lock +33 -32
  14. {lme_python-0.1.7 → lme_python-0.1.9}/Cargo.toml +30 -3
  15. {lme_python-0.1.7 → lme_python-0.1.9}/GUIDE.md +102 -8
  16. lme_python-0.1.9/OPTIMIZATION.md +488 -0
  17. {lme_python-0.1.7 → lme_python-0.1.9}/PKG-INFO +3 -1
  18. {lme_python-0.1.7 → lme_python-0.1.9}/README.md +18 -12
  19. lme_python-0.1.9/RELEASING.md +247 -0
  20. lme_python-0.1.9/REPO_COMPLETION_BY_AREA.md +102 -0
  21. lme_python-0.1.9/Taskfile.yml +154 -0
  22. lme_python-0.1.9/USABILITY.md +178 -0
  23. lme_python-0.1.9/benchmarks/fair-rust-julia-reference-2026-07-04.json +97 -0
  24. lme_python-0.1.9/benchmarks/fair-rust-julia-reference-2026-07-06.json +99 -0
  25. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/COMPARISONS.md +5 -2
  26. lme_python-0.1.9/comparisons/bench_fair_julia_perf.jl +95 -0
  27. lme_python-0.1.9/comparisons/bench_fair_julia_timing.jl +135 -0
  28. lme_python-0.1.9/comparisons/bench_fair_rust_julia.rs +464 -0
  29. lme_python-0.1.9/comparisons/bench_perf_breakdown.rs +141 -0
  30. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/categorical_anova.R +3 -2
  31. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/categorical_anova.jl +1 -1
  32. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_cbpp.R +8 -5
  33. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_cbpp.jl +9 -9
  34. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_gaussian.R +3 -6
  35. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_grouseticks.R +1 -0
  36. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_grouseticks.jl +11 -11
  37. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmer_weighted.R +1 -0
  38. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmer_weighted.jl +2 -1
  39. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_dyestuff.jl +2 -2
  40. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_pastes.R +2 -1
  41. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_pastes.jl +8 -8
  42. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_penicillin.jl +10 -10
  43. lme_python-0.1.9/comparisons/parity/glmm_offset.R +21 -0
  44. lme_python-0.1.9/comparisons/parity/glmm_offset.jl +27 -0
  45. lme_python-0.1.9/comparisons/parity/glmm_probit.R +21 -0
  46. lme_python-0.1.9/comparisons/parity/glmm_probit.jl +27 -0
  47. lme_python-0.1.9/comparisons/parity/glmm_weighted.R +22 -0
  48. lme_python-0.1.9/comparisons/parity/glmm_weighted.jl +27 -0
  49. lme_python-0.1.9/comparisons/parity/parity_r_setup.R +6 -0
  50. lme_python-0.1.9/comparisons/parity/sleepstudy_offset.R +21 -0
  51. lme_python-0.1.9/comparisons/parity/sleepstudy_offset.jl +27 -0
  52. lme_python-0.1.9/comparisons/parity_export.rs +125 -0
  53. lme_python-0.1.9/comparisons/r_setup.R +3 -0
  54. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy.R +1 -0
  55. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy.jl +12 -12
  56. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy_ml.jl +1 -1
  57. lme_python-0.1.9/examples/nlmm_fixture_dump.rs +40 -0
  58. lme_python-0.1.9/lefthook.yml +54 -0
  59. lme_python-0.1.9/mise.toml +16 -0
  60. {lme_python-0.1.7 → lme_python-0.1.9}/pyproject.toml +18 -0
  61. {lme_python-0.1.7 → lme_python-0.1.9}/python/Cargo.lock +41 -73
  62. {lme_python-0.1.7 → lme_python-0.1.9}/python/Cargo.toml +2 -2
  63. lme_python-0.1.9/python/PYTHON_GUIDE.md +368 -0
  64. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/glmer_cbpp.py +5 -4
  65. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/glmer_grouseticks.py +15 -14
  66. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/lm_basics.py +2 -2
  67. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/lmer_sleepstudy.py +9 -7
  68. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/model_comparison.py +22 -20
  69. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/compare_plots.py +22 -14
  70. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/find_rscript.py +1 -2
  71. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/numeric_overlay.py +1 -3
  72. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/plot_demo.py +9 -3
  73. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/run_all.py +10 -3
  74. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/parity.py +17 -21
  75. lme_python-0.1.9/python/lme_python.pyi +174 -0
  76. lme_python-0.1.9/python/src/lib.rs +1492 -0
  77. lme_python-0.1.9/python/tests/test_api_parity.py +189 -0
  78. {lme_python-0.1.7 → lme_python-0.1.9}/python/tests/test_basic.py +97 -66
  79. lme_python-0.1.9/python/tests/test_dataframe_inputs.py +45 -0
  80. lme_python-0.1.9/python/tests/test_golden_parity.py +112 -0
  81. lme_python-0.1.9/python/tests/test_nlmer_custom_mean.py +54 -0
  82. lme_python-0.1.9/python/uv.lock +2289 -0
  83. lme_python-0.1.9/scripts/ci/README.md +27 -0
  84. lme_python-0.1.9/scripts/ci/julia_format.jl +56 -0
  85. lme_python-0.1.9/scripts/ci/lme_ci.py +710 -0
  86. lme_python-0.1.9/scripts/ci/r_format.R +19 -0
  87. lme_python-0.1.9/scripts/ci/run.sh +30 -0
  88. lme_python-0.1.9/scripts/local_ci.ps1 +4 -0
  89. lme_python-0.1.9/scripts/local_ci.sh +4 -0
  90. lme_python-0.1.9/scripts/run_fair_rust_julia_benchmark.py +655 -0
  91. lme_python-0.1.9/scripts/run_perf_breakdown.py +342 -0
  92. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/sync_github_repo_metadata.py +35 -4
  93. lme_python-0.1.9/scripts/verify_cross_language_parity.py +331 -0
  94. {lme_python-0.1.7 → lme_python-0.1.9}/src/anova.rs +57 -169
  95. {lme_python-0.1.7 → lme_python-0.1.9}/src/anova_contrasts.rs +36 -2
  96. lme_python-0.1.9/src/contrast.rs +266 -0
  97. {lme_python-0.1.7 → lme_python-0.1.9}/src/ddf.rs +11 -5
  98. {lme_python-0.1.7 → lme_python-0.1.9}/src/family.rs +194 -33
  99. {lme_python-0.1.7 → lme_python-0.1.9}/src/glmm_math.rs +42 -186
  100. lme_python-0.1.9/src/intercept_blocked.rs +1237 -0
  101. {lme_python-0.1.7 → lme_python-0.1.9}/src/kenward_roger.rs +11 -1
  102. lme_python-0.1.9/src/kr_modcomp.rs +327 -0
  103. lme_python-0.1.9/src/kr_vcov_adj.rs +296 -0
  104. {lme_python-0.1.7 → lme_python-0.1.9}/src/lib.rs +274 -138
  105. lme_python-0.1.9/src/math.rs +1699 -0
  106. {lme_python-0.1.7 → lme_python-0.1.9}/src/model_matrix.rs +281 -64
  107. lme_python-0.1.9/src/nlmm/fit.rs +1075 -0
  108. lme_python-0.1.9/src/nlmm/formula.rs +284 -0
  109. lme_python-0.1.9/src/nlmm/mean.rs +25 -0
  110. lme_python-0.1.9/src/nlmm/mean_fn.rs +151 -0
  111. lme_python-0.1.9/src/nlmm/mod.rs +87 -0
  112. lme_python-0.1.9/src/nlmm/predict.rs +104 -0
  113. lme_python-0.1.9/src/nlmm/re_cov.rs +125 -0
  114. lme_python-0.1.9/src/nlmm/self_start.rs +214 -0
  115. lme_python-0.1.9/src/nlmm/ssasymp.rs +33 -0
  116. lme_python-0.1.9/src/nlmm/ssgompertz.rs +47 -0
  117. lme_python-0.1.9/src/nlmm/sslogis.rs +37 -0
  118. lme_python-0.1.9/src/nlmm/ssmicmen.rs +27 -0
  119. {lme_python-0.1.7 → lme_python-0.1.9}/src/optimizer.rs +351 -26
  120. lme_python-0.1.9/src/perf_diag.rs +302 -0
  121. lme_python-0.1.9/src/quadrature.rs +187 -0
  122. {lme_python-0.1.7 → lme_python-0.1.9}/src/satterthwaite.rs +2 -0
  123. lme_python-0.1.9/tests/data/cbpp_binary.csv +843 -0
  124. lme_python-0.1.9/tests/data/cbpp_binary_weighted.csv +843 -0
  125. lme_python-0.1.9/tests/data/glmm_binomial_probit.json +872 -0
  126. lme_python-0.1.9/tests/data/glmm_binomial_weighted.json +872 -0
  127. lme_python-0.1.9/tests/data/glmm_poisson_offset.json +536 -0
  128. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/golden_parity_manifest.json +372 -8
  129. lme_python-0.1.9/tests/data/grouseticks.csv +404 -0
  130. lme_python-0.1.9/tests/data/orange.csv +36 -0
  131. lme_python-0.1.9/tests/data/orange_nlmer.json +12 -0
  132. lme_python-0.1.9/tests/data/pastes_cask_reml.json +100 -0
  133. lme_python-0.1.9/tests/data/sleepstudy.csv +181 -0
  134. lme_python-0.1.9/tests/data/sleepstudy_offset_reml.json +246 -0
  135. lme_python-0.1.9/tests/data/ssasymp_synthetic.csv +61 -0
  136. lme_python-0.1.9/tests/data/ssfol_nlmer.json +10 -0
  137. lme_python-0.1.9/tests/data/ssgompertz_nlmer.json +10 -0
  138. lme_python-0.1.9/tests/data/ssgompertz_synthetic.csv +61 -0
  139. lme_python-0.1.9/tests/data/ssmicmen_nlmer.json +10 -0
  140. lme_python-0.1.9/tests/data/ssmicmen_synthetic.csv +61 -0
  141. lme_python-0.1.9/tests/gen_nlmm_extras.R +54 -0
  142. lme_python-0.1.9/tests/generate_nlmm_fixtures.R +65 -0
  143. lme_python-0.1.9/tests/generate_test_data.R +895 -0
  144. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_anova.rs +2 -0
  145. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_anova_types.rs +18 -0
  146. lme_python-0.1.9/tests/test_contrast.rs +87 -0
  147. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_coverage_gaps.rs +2 -0
  148. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_crossed_mock.rs +14 -0
  149. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_errors.rs +20 -21
  150. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_failure_modes.rs +14 -1
  151. lme_python-0.1.9/tests/test_glmm_links.rs +78 -0
  152. lme_python-0.1.9/tests/test_glmm_offset_grouseticks.rs +43 -0
  153. lme_python-0.1.9/tests/test_glmm_weighted.rs +64 -0
  154. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_golden_parity.rs +81 -12
  155. lme_python-0.1.9/tests/test_kr_modcomp_pastes.rs +51 -0
  156. lme_python-0.1.9/tests/test_nlmm_agq.rs +37 -0
  157. lme_python-0.1.9/tests/test_nlmm_custom_mean.rs +51 -0
  158. lme_python-0.1.9/tests/test_nlmm_orange.rs +154 -0
  159. lme_python-0.1.9/tests/test_nlmm_orange_multi_re.rs +92 -0
  160. lme_python-0.1.9/tests/test_nlmm_self_start.rs +43 -0
  161. lme_python-0.1.9/tests/test_nlmm_ssasymp.rs +79 -0
  162. lme_python-0.1.9/tests/test_nlmm_ssfol.rs +98 -0
  163. lme_python-0.1.9/tests/test_nlmm_ssmicmen.rs +37 -0
  164. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_production_load.rs +60 -3
  165. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_sandwich_math.R +3 -3
  166. lme_python-0.1.7/BENCHMARKS.md +0 -183
  167. lme_python-0.1.7/CHANGELOG.md +0 -103
  168. lme_python-0.1.7/CONTRIBUTING.md +0 -122
  169. lme_python-0.1.7/RELEASING.md +0 -208
  170. lme_python-0.1.7/REPO_COMPLETION_BY_AREA.md +0 -74
  171. lme_python-0.1.7/python/PYTHON_GUIDE.md +0 -266
  172. lme_python-0.1.7/python/requirements-ci.txt +0 -87
  173. lme_python-0.1.7/python/src/lib.rs +0 -675
  174. lme_python-0.1.7/scripts/local_ci.ps1 +0 -85
  175. lme_python-0.1.7/scripts/local_ci.sh +0 -86
  176. lme_python-0.1.7/src/math.rs +0 -420
  177. lme_python-0.1.7/tests/data/cbpp_binary.csv +0 -843
  178. lme_python-0.1.7/tests/data/grouseticks.csv +0 -404
  179. lme_python-0.1.7/tests/data/pastes_cask_reml.json +0 -13
  180. lme_python-0.1.7/tests/data/sleepstudy.csv +0 -181
  181. lme_python-0.1.7/tests/generate_test_data.R +0 -506
  182. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/crate-publish-dry-run.yml +0 -0
  183. {lme_python-0.1.7 → lme_python-0.1.9}/.github/workflows/fuzz-smoke.yml +0 -0
  184. {lme_python-0.1.7 → lme_python-0.1.9}/LICENSE +0 -0
  185. {lme_python-0.1.7 → lme_python-0.1.9}/benches/bench_load_production.rs +0 -0
  186. {lme_python-0.1.7 → lme_python-0.1.9}/benches/bench_math.rs +0 -0
  187. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/categorical_anova.py +0 -0
  188. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/categorical_anova.rs +0 -0
  189. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/comparison_chart.png +0 -0
  190. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_cbpp.py +0 -0
  191. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_cbpp.rs +0 -0
  192. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_gaussian.jl +0 -0
  193. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_gaussian.py +0 -0
  194. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_gaussian.rs +0 -0
  195. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_grouseticks.py +0 -0
  196. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/glmm_grouseticks.rs +0 -0
  197. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmer_weighted.py +0 -0
  198. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmer_weighted.rs +0 -0
  199. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_dyestuff.R +0 -0
  200. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_dyestuff.py +0 -0
  201. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_dyestuff.rs +0 -0
  202. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_pastes.py +0 -0
  203. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_pastes.rs +0 -0
  204. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_penicillin.R +0 -0
  205. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_penicillin.py +0 -0
  206. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/lmm_penicillin.rs +0 -0
  207. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy.py +0 -0
  208. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy.rs +0 -0
  209. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy_ml.R +0 -0
  210. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy_ml.py +0 -0
  211. {lme_python-0.1.7 → lme_python-0.1.9}/comparisons/sleepstudy_ml.rs +0 -0
  212. {lme_python-0.1.7 → lme_python-0.1.9}/docs/benchmarks/app.js +0 -0
  213. {lme_python-0.1.7 → lme_python-0.1.9}/docs/benchmarks/index.html +0 -0
  214. {lme_python-0.1.7 → lme_python-0.1.9}/docs/benchmarks/styles.css +0 -0
  215. {lme_python-0.1.7 → lme_python-0.1.9}/docs/index.html +0 -0
  216. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/.gitignore +0 -0
  217. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/README.md +0 -0
  218. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/figure_specs.py +0 -0
  219. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/paths.py +0 -0
  220. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/plotting_demo/plot_r.R +0 -0
  221. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/README.md +0 -0
  222. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/conftest.py +0 -0
  223. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/paths.py +0 -0
  224. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/run.py +0 -0
  225. {lme_python-0.1.7 → lme_python-0.1.9}/python/examples/verification_project/test_parity.py +0 -0
  226. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/ast_explorations/test_fiasto.rs +0 -0
  227. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
  228. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/build_benchmark_site.py +0 -0
  229. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/dump_dyestuff.R +0 -0
  230. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/dump_pastes.R +0 -0
  231. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/make_penicillin_csv.py +0 -0
  232. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/plot_comparisons.py +0 -0
  233. {lme_python-0.1.7 → lme_python-0.1.9}/scripts/run_cross_language_benchmarks.py +0 -0
  234. {lme_python-0.1.7 → lme_python-0.1.9}/src/formula.rs +0 -0
  235. {lme_python-0.1.7 → lme_python-0.1.9}/src/robust.rs +0 -0
  236. {lme_python-0.1.7 → lme_python-0.1.9}/tests/categorical_anova_test.rs +0 -0
  237. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/dyestuff.csv +0 -0
  238. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/glmm_binomial.json +0 -0
  239. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/glmm_poisson.json +0 -0
  240. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/intercept_only.json +0 -0
  241. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/mock_crossed.json +0 -0
  242. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/mock_ml.json +0 -0
  243. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/pastes.csv +0 -0
  244. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/penicillin.csv +0 -0
  245. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/penicillin.json +0 -0
  246. {lme_python-0.1.7 → lme_python-0.1.9}/tests/data/random_slopes.json +0 -0
  247. {lme_python-0.1.7 → lme_python-0.1.9}/tests/generate_mock_data.py +0 -0
  248. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_conditional_real.rs +0 -0
  249. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_confint_simulate.rs +0 -0
  250. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_coverage_edge_cases.rs +0 -0
  251. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_e2e_lmer.rs +0 -0
  252. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_edge_cases.rs +0 -0
  253. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_formula.rs +0 -0
  254. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_formula_stress.rs +0 -0
  255. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_gaps.rs +0 -0
  256. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_glmm.rs +0 -0
  257. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_intercept_only.rs +0 -0
  258. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_kenward_roger.rs +0 -0
  259. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_ml_optimization.rs +0 -0
  260. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_no_intercept.rs +0 -0
  261. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_numerical_parity.rs +0 -0
  262. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_predict.rs +0 -0
  263. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_random_slopes.rs +0 -0
  264. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_robust.rs +0 -0
  265. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_satterthwaite.rs +0 -0
  266. {lme_python-0.1.7 → lme_python-0.1.9}/tests/test_statistical_identities.rs +0 -0
@@ -0,0 +1,8 @@
1
+ # JuliaFormatter defaults for comparison / parity scripts under comparisons/.
2
+ indent = 4
3
+ margin = 92
4
+ always_for_in = true
5
+ whitespace_typedefs = true
6
+ whitespace_ops_in_indices = true
7
+ remove_extra_newlines = true
8
+ join_lines_based_on_source = true
@@ -1,24 +1,10 @@
1
1
  name: Security audit
2
2
 
3
3
  on:
4
- schedule:
5
- # Weekly: Mondays 12:00 UTC
6
- - cron: "0 12 * * 1"
7
4
  workflow_dispatch:
8
5
  push:
9
- branches: [master]
10
- paths:
11
- - "**/Cargo.toml"
12
- - "**/Cargo.lock"
13
- - "python/pyproject.toml"
14
- - "python/requirements-ci.txt"
15
- pull_request:
16
- branches: [master]
17
- paths:
18
- - "**/Cargo.toml"
19
- - "**/Cargo.lock"
20
- - "python/pyproject.toml"
21
- - "python/requirements-ci.txt"
6
+ tags:
7
+ - "v*"
22
8
 
23
9
  permissions:
24
10
  contents: read
@@ -49,25 +35,21 @@ jobs:
49
35
  run: cargo audit
50
36
 
51
37
  pip-audit:
52
- name: pip-audit (python lockfile)
38
+ name: pip-audit (uv.lock)
53
39
  runs-on: ubuntu-latest
54
40
  steps:
55
41
  - uses: actions/checkout@v6
56
42
 
57
- - name: Set up Python
58
- uses: actions/setup-python@v6
43
+ - name: Set up uv
44
+ uses: astral-sh/setup-uv@v6
59
45
  with:
60
46
  python-version: "3.11"
61
- cache: pip
62
- cache-dependency-path: python/requirements-ci.txt
47
+ enable-cache: true
48
+ cache-dependency-glob: "python/uv.lock"
63
49
 
64
- - name: Install locked CI dependencies
50
+ - name: pip-audit
65
51
  working-directory: python
66
52
  run: |
67
53
  set -euo pipefail
68
- python -m pip install -U pip
69
- python -m pip install -r requirements-ci.txt
70
-
71
- - name: pip-audit
72
- working-directory: python
73
- run: python -m pip_audit
54
+ uv sync --extra dev --no-install-project
55
+ uv run pip-audit
@@ -122,13 +122,24 @@ jobs:
122
122
  - name: Install R benchmark dependencies
123
123
  run: |
124
124
  mkdir -p "$R_LIBS_USER"
125
- Rscript -e 'install.packages("lme4", repos="https://cloud.r-project.org", lib=Sys.getenv("R_LIBS_USER"))'
125
+ Rscript -e 'install.packages(c("lme4", "lmerTest", "car", "rlang", "styler"), repos="https://cloud.r-project.org", lib=Sys.getenv("R_LIBS_USER"))'
126
126
 
127
127
  - name: Install Julia benchmark dependencies
128
- run: julia -e 'using Pkg; Pkg.add(["CSV", "DataFrames", "MixedModels"])'
128
+ run: julia -e 'using Pkg; Pkg.add(["CSV", "DataFrames", "JSON", "MixedModels", "GLM", "JuliaFormatter"])'
129
+
130
+ - name: Check comparison script formatting
131
+ run: python3 scripts/ci/lme_ci.py comparison-format-check --required
129
132
 
130
133
  - name: Build Rust examples
131
- run: cargo build --release --examples
134
+ run: cargo build --release --locked --examples
135
+
136
+ - name: Run fair Rust vs Julia fit benchmark
137
+ run: |
138
+ python scripts/run_fair_rust_julia_benchmark.py \
139
+ --warmups "$BENCH_WARMUPS" \
140
+ --repeats "$BENCH_REPEATS" \
141
+ --output "benchmark-results/fair-rust-julia-${{ github.sha }}.json" \
142
+ --skip-rust-build
132
143
 
133
144
  - name: Run cross-language benchmark script
134
145
  id: cross_language_run
@@ -149,7 +160,9 @@ jobs:
149
160
  uses: actions/upload-artifact@v7
150
161
  with:
151
162
  name: cross-language-${{ github.sha }}
152
- path: benchmark-results/cross-language-${{ github.sha }}.json
163
+ path: |
164
+ benchmark-results/cross-language-${{ github.sha }}.json
165
+ benchmark-results/fair-rust-julia-${{ github.sha }}.json
153
166
 
154
167
  - name: Attach cross-language artifact to GitHub Release
155
168
  if: always() && startsWith(github.ref, 'refs/tags/')
@@ -164,10 +177,17 @@ jobs:
164
177
  fi
165
178
  }
166
179
  gh release upload "${GITHUB_REF_NAME}" "benchmark-results/cross-language-${{ github.sha }}.json" --clobber
180
+ if [ -f "benchmark-results/fair-rust-julia-${{ github.sha }}.json" ]; then
181
+ gh release upload "${GITHUB_REF_NAME}" "benchmark-results/fair-rust-julia-${{ github.sha }}.json" --clobber
182
+ fi
167
183
 
168
184
  - name: Fail if cross-language benchmarks failed
169
185
  if: steps.cross_language_run.outputs.status != '0'
170
186
  run: |
187
+ json="benchmark-results/cross-language-${{ github.sha }}.json"
188
+ if [ -f "$json" ]; then
189
+ python3 scripts/ci/lme_ci.py benchmark-failures "$json" >&2 || true
190
+ fi
171
191
  echo "Cross-language benchmark script failed with exit code ${{ steps.cross_language_run.outputs.status }}." >&2
172
192
  exit 1
173
193
 
@@ -2,15 +2,9 @@ name: CI
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [master]
6
- paths-ignore:
7
- - "**/*.md"
8
- - "LICENSE"
9
- pull_request:
10
- branches: [master]
11
- paths-ignore:
12
- - "**/*.md"
13
- - "LICENSE"
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch:
14
8
 
15
9
  permissions:
16
10
  contents: read
@@ -23,6 +17,28 @@ env:
23
17
  CARGO_TERM_COLOR: always
24
18
 
25
19
  jobs:
20
+ rust-lint:
21
+ name: Rust fmt & clippy
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6
25
+ - uses: dtolnay/rust-toolchain@stable
26
+ - name: Install OpenBLAS
27
+ run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
28
+ - name: Cache cargo registry & build
29
+ uses: actions/cache@v5
30
+ with:
31
+ path: |
32
+ ~/.cargo/registry
33
+ ~/.cargo/git
34
+ target
35
+ key: ubuntu-lint-${{ hashFiles('**/Cargo.lock') }}
36
+ restore-keys: ubuntu-lint-
37
+ - name: rustfmt --check
38
+ run: python3 scripts/ci/lme_ci.py fmt-check
39
+ - name: clippy
40
+ run: python3 scripts/ci/lme_ci.py clippy
41
+
26
42
  test:
27
43
  name: Test (${{ matrix.os }})
28
44
  runs-on: ${{ matrix.os }}
@@ -37,10 +53,6 @@ jobs:
37
53
  - name: Install Rust toolchain
38
54
  uses: dtolnay/rust-toolchain@stable
39
55
 
40
- - name: Install OpenBLAS (macOS)
41
- if: matrix.os == 'macos-latest'
42
- run: brew install openblas
43
-
44
56
  - name: Install OpenBLAS (Ubuntu)
45
57
  if: matrix.os == 'ubuntu-latest'
46
58
  run: sudo apt-get update && sudo apt-get install -y libopenblas-dev
@@ -55,35 +67,29 @@ jobs:
55
67
  key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
56
68
  restore-keys: ${{ runner.os }}-cargo-
57
69
 
58
- - name: Build
59
- run: cargo build --verbose --locked
70
+ - name: Set OpenBLAS target (macOS)
71
+ if: matrix.os == 'macos-latest'
72
+ run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
60
73
 
61
74
  - name: Run tests
62
75
  run: cargo test --verbose --locked
63
76
 
64
- - name: Set up Python
65
- uses: actions/setup-python@v6
77
+ - name: Set up uv
78
+ uses: astral-sh/setup-uv@v6
66
79
  with:
67
80
  python-version: "3.11"
68
- cache: pip
69
- cache-dependency-path: python/requirements-ci.txt
81
+ enable-cache: true
82
+ cache-dependency-glob: "python/uv.lock"
70
83
 
71
84
  - name: Python bindings (maturin develop, pytest, wheel install, pytest)
72
85
  working-directory: python
73
86
  shell: bash
74
87
  run: |
75
88
  set -euo pipefail
76
- python -m venv .venv
77
- if [ -f .venv/Scripts/python.exe ]; then
78
- VPY=.venv/Scripts/python.exe
79
- else
80
- VPY=.venv/bin/python
81
- fi
82
- "$VPY" -m pip install -U pip
83
- "$VPY" -m pip install -r requirements-ci.txt
84
- "$VPY" -m maturin develop --release
85
- "$VPY" -m pytest tests/ -v
86
- "$VPY" -m maturin build --release -o dist
89
+ uv sync --extra dev --no-install-project
90
+ uv run maturin develop --release
91
+ uv run pytest tests/ -v
92
+ uv run maturin build --release -o dist
87
93
  shopt -s nullglob
88
94
  wheels=(dist/lme_python-*.whl)
89
95
  if [ ${#wheels[@]} -eq 0 ]; then
@@ -91,8 +97,8 @@ jobs:
91
97
  ls -la dist 2>/dev/null || true
92
98
  exit 1
93
99
  fi
94
- "$VPY" -m pip install --force-reinstall "${wheels[0]}"
95
- "$VPY" -m pytest tests/ -v
100
+ uv run pip install --force-reinstall "${wheels[0]}"
101
+ uv run pytest tests/ -v
96
102
 
97
103
  rust-all-targets:
98
104
  name: Rust check (all targets)
@@ -183,28 +189,22 @@ jobs:
183
189
  target
184
190
  key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
185
191
  restore-keys: ubuntu-cargo-
186
- - name: Set up Python
187
- uses: actions/setup-python@v6
192
+ - name: Set up uv
193
+ uses: astral-sh/setup-uv@v6
188
194
  with:
189
195
  python-version: ${{ matrix.python-version }}
190
- cache: pip
191
- cache-dependency-path: python/requirements-ci.txt
196
+ enable-cache: true
197
+ cache-dependency-glob: "python/uv.lock"
198
+
192
199
  - name: Python bindings (maturin develop, pytest, wheel install, pytest)
193
200
  working-directory: python
194
201
  shell: bash
195
202
  run: |
196
203
  set -euo pipefail
197
- python -m venv .venv
198
- if [ -f .venv/Scripts/python.exe ]; then
199
- VPY=.venv/Scripts/python.exe
200
- else
201
- VPY=.venv/bin/python
202
- fi
203
- "$VPY" -m pip install -U pip
204
- "$VPY" -m pip install -r requirements-ci.txt
205
- "$VPY" -m maturin develop --release
206
- "$VPY" -m pytest tests/ -v
207
- "$VPY" -m maturin build --release -o dist
204
+ uv sync --extra dev --no-install-project
205
+ uv run maturin develop --release
206
+ uv run pytest tests/ -v
207
+ uv run maturin build --release -o dist
208
208
  shopt -s nullglob
209
209
  wheels=(dist/lme_python-*.whl)
210
210
  if [ ${#wheels[@]} -eq 0 ]; then
@@ -212,8 +212,8 @@ jobs:
212
212
  ls -la dist 2>/dev/null || true
213
213
  exit 1
214
214
  fi
215
- "$VPY" -m pip install --force-reinstall "${wheels[0]}"
216
- "$VPY" -m pytest tests/ -v
215
+ uv run pip install --force-reinstall "${wheels[0]}"
216
+ uv run pytest tests/ -v
217
217
 
218
218
  docs:
219
219
  name: Build docs
@@ -1,16 +1,10 @@
1
1
  name: Build and Publish Python Wheels
2
2
 
3
- # Trigger on pushes to the master branch or when a release tag (v*) is pushed
3
+ # Trigger automatically only for release tags; use workflow_dispatch for manual builds.
4
4
  on:
5
5
  push:
6
- branches:
7
- - master
8
6
  tags:
9
- - 'v*'
10
- pull_request:
11
- paths:
12
- - 'python/**'
13
- - 'src/**'
7
+ - "v*"
14
8
  workflow_dispatch:
15
9
 
16
10
  permissions:
@@ -82,8 +76,9 @@ jobs:
82
76
  - uses: actions/setup-python@v6
83
77
  with:
84
78
  python-version: '3.10'
85
- - name: Install OpenBLAS
86
- run: brew install openblas
79
+ - name: Set OpenBLAS target (Apple Silicon wheel)
80
+ if: matrix.target == 'aarch64'
81
+ run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
87
82
  - name: Build wheels
88
83
  uses: PyO3/maturin-action@v1
89
84
  with:
@@ -2,11 +2,8 @@ name: Sync Repository Metadata
2
2
 
3
3
  on:
4
4
  push:
5
- branches: [master]
6
- paths:
7
- - Cargo.toml
8
- - scripts/sync_github_repo_metadata.py
9
- - .github/workflows/repo-metadata.yml
5
+ tags:
6
+ - "v*"
10
7
  workflow_dispatch:
11
8
 
12
9
  permissions:
@@ -24,10 +21,20 @@ jobs:
24
21
  run: |
25
22
  if [ -z "$REPO_ADMIN_TOKEN" ]; then
26
23
  echo "REPO_ADMIN_TOKEN is not set."
27
- echo "Create a fine-grained PAT or GitHub App token with repository Administration: write."
24
+ echo "Create a fine-grained PAT with repository Administration: Read and write."
25
+ echo "Add it under Settings → Secrets and variables → Actions → REPO_ADMIN_TOKEN."
28
26
  exit 1
29
27
  fi
30
28
 
29
+ - name: Dry-run metadata payload
30
+ run: python scripts/sync_github_repo_metadata.py --dry-run
31
+
32
+ - name: Verify admin token is valid
33
+ env:
34
+ REPO_ADMIN_TOKEN: ${{ secrets.REPO_ADMIN_TOKEN }}
35
+ GITHUB_REPOSITORY: ${{ github.repository }}
36
+ run: python scripts/sync_github_repo_metadata.py --verify-token
37
+
31
38
  - name: Sync repository metadata
32
39
  env:
33
40
  REPO_ADMIN_TOKEN: ${{ secrets.REPO_ADMIN_TOKEN }}
@@ -10,6 +10,7 @@ flamegraph.svg
10
10
  artifacts/
11
11
  benchmark-results/
12
12
  benchmark-site/
13
+ .r-lib/
13
14
  site/
14
15
 
15
16
  # Python bindings
@@ -0,0 +1,192 @@
1
+ # Agent and contributor pre-flights
2
+
3
+ This repo uses **four tiers** of checks (one more than a typical Task + bash-hook setup). Pick the tier that matches how much you changed.
4
+
5
+ ## Architecture (vs duplicated shell scripts)
6
+
7
+ ```mermaid
8
+ flowchart TD
9
+ subgraph entry [Entry points]
10
+ task[Taskfile.yml]
11
+ lh[Lefthook]
12
+ gha[GitHub Actions]
13
+ legacy[scripts/local_ci.*]
14
+ end
15
+
16
+ subgraph core [Single implementation]
17
+ lme["scripts/ci/lme_ci.py"]
18
+ end
19
+
20
+ subgraph tools [Pinned tooling via mise.toml]
21
+ mise[mise install]
22
+ uv[uv — Python venv + Ruff]
23
+ lk[lefthook — git hooks]
24
+ end
25
+
26
+ task --> lme
27
+ lh --> lme
28
+ gha --> lme
29
+ legacy --> lme
30
+ mise --> uv
31
+ mise --> lk
32
+ ```
33
+
34
+ **Why this is better than parallel `.ps1` / `.sh` trees:**
35
+
36
+ | Piece | Role |
37
+ |---|---|
38
+ | [`scripts/ci/lme_ci.py`](lme_ci.py) | One cross-platform implementation (stdlib Python 3.10+) |
39
+ | [`lefthook.yml`](../lefthook.yml) | Declarative hooks, **parallel** pre-commit, `stage_fixed` auto-staging |
40
+ | [`mise.toml`](../mise.toml) | Pins Rust, Python 3.11, uv, lefthook, task — reproducible dev env |
41
+ | [`uv`](https://docs.astral.sh/uv/) | Locked Python env (`uv sync` + `uv.lock`); Ruff via `uv tool run` |
42
+ | **Ruff** | `python/tests` + `python/examples` on `task lint`; staged auto-fix on commit |
43
+ | **pre-push hook** | Automatic `preflight` (lint + `cargo check --all-targets` + `cargo audit` + repo metadata dry-run) |
44
+
45
+ [`Taskfile.yml`](../Taskfile.yml) is a thin alias layer; it does not duplicate logic.
46
+
47
+ ## 0. One-time setup
48
+
49
+ ```powershell
50
+ mise install # rust, python 3.11, uv, lefthook, task
51
+ task setup # mise install + lefthook install
52
+ ```
53
+
54
+ Or install tools manually, then:
55
+
56
+ ```powershell
57
+ task hooks:install
58
+ ```
59
+
60
+ ## 1. On `git commit` (Lefthook — parallel, staged-only)
61
+
62
+ [`lefthook.yml`](../lefthook.yml) runs only when matching files are staged:
63
+
64
+ | Staged | Command | Notes |
65
+ |---|---|---|
66
+ | `**/*.rs` | `fmt` then `clippy` | `stage_fixed: true` re-stages rustfmt fixes |
67
+ | `python/**/*.py` | `ruff-staged --fix` | Ruff check + format; auto-stages fixes |
68
+ | `comparisons/**/*.R`, `tests/*.R` | `r-format-staged --fix` | **styler** when Rscript + styler are installed; skips otherwise |
69
+ | `comparisons/**/*.jl` | `julia-format-staged --fix` | **JuliaFormatter** when julia + JuliaFormatter are installed; skips otherwise |
70
+ | `Cargo.toml`, `Cargo.lock`, `python/Cargo.toml`, `python/Cargo.lock` | `cargo check --all-targets` | Catches link/BLAS issues on your platform when manifests change |
71
+ | `Cargo.toml`, `scripts/sync_github_repo_metadata.py` | `repo-metadata` dry-run (+ token verify if `REPO_ADMIN_TOKEN` set) | Validates About-box payload before GHA metadata sync |
72
+ | `comparisons/**`, benchmark script/workflow | `benchmarks-smoke` | Rust sleepstudy example timing (release build) |
73
+
74
+ Runs **in parallel** where safe (Ruff vs Rust fmt). Clippy waits on fmt (`priority`); manifest `check` runs after clippy (`priority` 3).
75
+
76
+ **Not** on commit: full `cargo test`, maturin/pytest, `pip-audit`.
77
+
78
+ Bypass once: `git commit --no-verify`.
79
+
80
+ ## 2. On `git push` (Lefthook pre-push)
81
+
82
+ Runs **`preflight`** in parallel with itself as one command:
83
+
84
+ 1. `lint` — Rust fmt/clippy + Ruff
85
+ 2. `cargo check --workspace --all-targets --locked` — compile graph including tests/examples
86
+ 3. `cargo audit` — root crate + `python/` (same as GHA audit job, without `pip-audit`)
87
+ 4. `repo-metadata` — dry-run from `Cargo.toml`; **`--verify-token`** when `REPO_ADMIN_TOKEN` is in the environment
88
+
89
+ Install `cargo-audit` once: `cargo install cargo-audit` (pinned in GHA as 0.22.1).
90
+
91
+ To catch **expired metadata tokens** before push (optional):
92
+
93
+ ```powershell
94
+ $env:REPO_ADMIN_TOKEN = "<fine-grained PAT with Administration: Read and write>"
95
+ task repo-metadata
96
+ ```
97
+
98
+ If GHA reports `401 Bad credentials` on **Sync Repository Metadata**, rotate the secret: **Settings → Secrets and variables → Actions → `REPO_ADMIN_TOKEN`**.
99
+
100
+ Bypass: `git push --no-verify`.
101
+
102
+ **macOS Apple Silicon BLAS** (`openblas-static` vs MKL) is only exercised on `macos-latest` in GHA — Windows/Linux preflights cannot catch that matrix cell. GHA now runs automatically only for `v*` tags (plus manual dispatch), so after changing `Cargo.toml` BLAS target tables or release workflows, run `task ci` locally or manually dispatch the CI workflow before tagging.
103
+
104
+ **Cross-language benchmarks** (R + Julia, tag-only [`.github/workflows/benchmarks.yml`](.github/workflows/benchmarks.yml)): pre-commit runs `benchmarks-smoke` when `comparisons/**` changes (Rust `sleepstudy` example only). Full matrix needs R (`lme4`, `lmerTest`, `car`, `rlang`, `styler`) and Julia (`JuliaFormatter`) — run it through the tag/manual GHA workflow or `task benchmarks:preflight` with R installed. Comparison script formatting is checked in that GHA job; locally use `task lint:comparisons` (skips when tools are missing) or `task lint:comparisons:required` when R/Julia formatters are installed.
105
+
106
+ ## 3. Before finishing work (manual)
107
+
108
+ If you changed **LMM fit throughput** ([`src/math.rs`](src/math.rs) intercept path, [`src/optimizer.rs`](src/optimizer.rs) θ search), read **[OPTIMIZATION.md](OPTIMIZATION.md)** and re-run the fair harness cases listed there.
109
+
110
+ ### Quick path
111
+
112
+ ```powershell
113
+ task lint
114
+ task test:fast # unit tests only (~seconds after compile)
115
+ task preflight # same as pre-push hook (without pip-audit)
116
+ task audit # full security audit incl. pip-audit
117
+ ```
118
+
119
+ Full integration tests: `task test` or `cargo test --locked` (uses `[profile.test] opt-level = 2` in [`Cargo.toml`](Cargo.toml)).
120
+
121
+ Runs Rust (`fmt --check` + clippy) and Python Ruff on `python/tests` and `python/examples`.
122
+
123
+ If you changed **Python bindings**:
124
+
125
+ ```powershell
126
+ task python
127
+ ```
128
+
129
+ ### Rust-only
130
+
131
+ ```powershell
132
+ task rust
133
+ ```
134
+
135
+ ### Python-only
136
+
137
+ ```powershell
138
+ task lint:python
139
+ task python
140
+ ```
141
+
142
+ Direct runner (no Task):
143
+
144
+ ```powershell
145
+ python scripts/ci/lme_ci.py lint
146
+ python scripts/ci/lme_ci.py python --reuse-venv
147
+ ```
148
+
149
+ ## 4. Before PR / large refactors
150
+
151
+ ```powershell
152
+ task # full core CI mirror
153
+ task ci:fast # reuse python/.venv, skip wheel-reinstall pytest
154
+ ```
155
+
156
+ Equivalent:
157
+
158
+ ```powershell
159
+ python scripts/ci/lme_ci.py ci
160
+ python scripts/ci/lme_ci.py ci --reuse-venv --skip-wheel-reinstall
161
+ ```
162
+
163
+ **GHA-only:** multi-OS matrix, Python 3.10/3.12/3.13, production-load gates, and `pip-audit`. These workflows are release-oriented: automatic runs are limited to `v*` tags, with manual dispatch available for pre-release checks.
164
+
165
+ ## `lme_ci.py` commands
166
+
167
+ | Command | Purpose |
168
+ |---|---|
169
+ | `ci` | Full core CI |
170
+ | `lint` | Rust + Python static checks |
171
+ | `preflight` | Pre-push: lint + check + cargo audit + repo metadata |
172
+ | `audit` | cargo audit + pip-audit (GHA mirror) |
173
+ | `repo-metadata` | Dry-run GitHub About sync; verify token if `REPO_ADMIN_TOKEN` set |
174
+ | `benchmarks-smoke` | Release examples + one Rust cross-language benchmark |
175
+ | `benchmarks-preflight` | `benchmarks-smoke` + optional `sleepstudy.R` when R/lme4 present |
176
+ | `benchmarks-fair-rust-julia` | Fair fit-only Rust vs MixedModels.jl timing (tier-A cases; see [BENCHMARK_COVERAGE.md](../BENCHMARK_COVERAGE.md); skip if Julia/packages missing) |
177
+ | `perf-breakdown` | Rust `LME_PERF_DIAG` vs Julia `optsum.feval`; reports `prepare` / `fit_prepared` / cold `lmer` |
178
+ | `rust-lint` | `fmt --check` + clippy |
179
+ | `ruff-lint` | Ruff on `python/tests` + `python/examples` |
180
+ | `comparison-format-check` | Optional styler/JuliaFormatter check on `comparisons/` (+ `tests/*.R`); use `--required` in GHA |
181
+ | `r-format-staged --fix` | Staged R comparison/golden-parity scripts |
182
+ | `julia-format-staged --fix` | Staged Julia comparison scripts |
183
+ | `build-test` | `cargo test` (full suite) |
184
+ | `test-fast` | `cargo test --lib` only (quick unit tests) |
185
+ | `rust-all` | Rust slice without Python bindings tests |
186
+ | `python` | Maturin + pytest (+ wheel pass) |
187
+ | `ruff-staged --fix` | Staged Python lint/format |
188
+ | `hooks-install` | `lefthook install` |
189
+
190
+ Run `python scripts/ci/lme_ci.py --help` for the full list.
191
+
192
+ See also [`CONTRIBUTING.md`](../CONTRIBUTING.md).