lme-python 0.1.8__tar.gz → 0.1.11__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 (307) hide show
  1. lme_python-0.1.11/.JuliaFormatter.toml +8 -0
  2. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/audit.yml +12 -28
  3. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/benchmarks.yml +24 -4
  4. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/ci.yml +27 -46
  5. lme_python-0.1.11/.github/workflows/crate-publish-dry-run.yml +43 -0
  6. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/python-release.yml +39 -8
  7. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/repo-metadata.yml +5 -5
  8. {lme_python-0.1.8 → lme_python-0.1.11}/.gitignore +1 -0
  9. {lme_python-0.1.8 → lme_python-0.1.11}/AGENTS.md +26 -4
  10. lme_python-0.1.11/BENCHMARKS.md +460 -0
  11. lme_python-0.1.11/BENCHMARK_COVERAGE.md +109 -0
  12. lme_python-0.1.11/CHANGELOG.md +205 -0
  13. {lme_python-0.1.8 → lme_python-0.1.11}/CONTRIBUTING.md +60 -24
  14. {lme_python-0.1.8 → lme_python-0.1.11}/Cargo.lock +31 -30
  15. {lme_python-0.1.8 → lme_python-0.1.11}/Cargo.toml +28 -3
  16. {lme_python-0.1.8 → lme_python-0.1.11}/GUIDE.md +194 -18
  17. lme_python-0.1.11/LICENSES/Apache-2.0.txt +193 -0
  18. lme_python-0.1.11/LICENSES/BSD-3-Clause-OpenBLAS.txt +31 -0
  19. lme_python-0.1.11/LICENSES/GPL-2.0-or-later.txt +342 -0
  20. lme_python-0.1.11/LICENSES/Intel-Simplified-Software-License.txt +79 -0
  21. lme_python-0.1.11/LICENSES/LGPL-2.1-only.txt +412 -0
  22. lme_python-0.1.11/OPTIMIZATION.md +530 -0
  23. {lme_python-0.1.8 → lme_python-0.1.11}/PKG-INFO +3 -1
  24. lme_python-0.1.11/README.md +111 -0
  25. {lme_python-0.1.8 → lme_python-0.1.11}/RELEASING.md +33 -46
  26. lme_python-0.1.11/RELINKING.md +22 -0
  27. lme_python-0.1.11/REPO_COMPLETION_BY_AREA.md +102 -0
  28. lme_python-0.1.11/THIRD_PARTY_NOTICES.md +18 -0
  29. lme_python-0.1.11/Taskfile.yml +194 -0
  30. lme_python-0.1.11/USABILITY.md +183 -0
  31. {lme_python-0.1.8 → lme_python-0.1.11}/benches/bench_math.rs +3 -7
  32. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-04.json +97 -0
  33. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-06.json +99 -0
  34. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-08.json +82 -0
  35. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-direct-intercept-gram.json +24 -0
  36. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-full-lmm.json +46 -0
  37. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-glmm.json +26 -0
  38. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-intercept-setup.json +26 -0
  39. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes-linear-cache.json +23 -0
  40. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes.json +23 -0
  41. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-sleepstudy-slopes.json +54 -0
  42. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09.json +83 -0
  43. lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-10-cold-setup-followup.json +24 -0
  44. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/COMPARISONS.md +18 -2
  45. lme_python-0.1.11/comparisons/bench_fair_julia_perf.jl +95 -0
  46. lme_python-0.1.11/comparisons/bench_fair_julia_timing.jl +135 -0
  47. lme_python-0.1.11/comparisons/bench_fair_rust_julia.rs +493 -0
  48. lme_python-0.1.11/comparisons/bench_perf_breakdown.rs +147 -0
  49. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.R +3 -2
  50. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.jl +1 -1
  51. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.R +8 -5
  52. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.jl +9 -9
  53. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.R +3 -6
  54. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.R +1 -0
  55. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.jl +11 -11
  56. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.R +1 -0
  57. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.jl +2 -1
  58. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.jl +2 -2
  59. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.R +2 -1
  60. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.jl +8 -8
  61. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.jl +10 -10
  62. lme_python-0.1.11/comparisons/nlmm_sspower.R +60 -0
  63. lme_python-0.1.11/comparisons/parity/glmm_offset.R +21 -0
  64. lme_python-0.1.11/comparisons/parity/glmm_offset.jl +27 -0
  65. lme_python-0.1.11/comparisons/parity/glmm_probit.R +21 -0
  66. lme_python-0.1.11/comparisons/parity/glmm_probit.jl +27 -0
  67. lme_python-0.1.11/comparisons/parity/glmm_weighted.R +22 -0
  68. lme_python-0.1.11/comparisons/parity/glmm_weighted.jl +27 -0
  69. lme_python-0.1.11/comparisons/parity/parity_r_setup.R +6 -0
  70. lme_python-0.1.11/comparisons/parity/sleepstudy_offset.R +21 -0
  71. lme_python-0.1.11/comparisons/parity/sleepstudy_offset.jl +27 -0
  72. lme_python-0.1.11/comparisons/parity_export.rs +120 -0
  73. lme_python-0.1.11/comparisons/r_setup.R +3 -0
  74. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.R +1 -0
  75. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.jl +12 -12
  76. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.jl +1 -1
  77. lme_python-0.1.11/docs/CALO_CALIBRATION.md +124 -0
  78. lme_python-0.1.11/examples/batch_sspower_cpu.rs +221 -0
  79. lme_python-0.1.11/examples/nlmm_fixture_dump.rs +51 -0
  80. {lme_python-0.1.8 → lme_python-0.1.11}/lefthook.yml +14 -0
  81. lme_python-0.1.11/legal/dependency-license-exceptions.json +17 -0
  82. lme_python-0.1.11/legal/fixture-provenance.json +40 -0
  83. {lme_python-0.1.8 → lme_python-0.1.11}/pyproject.toml +5 -0
  84. {lme_python-0.1.8 → lme_python-0.1.11}/python/Cargo.lock +26 -25
  85. {lme_python-0.1.8 → lme_python-0.1.11}/python/Cargo.toml +1 -1
  86. lme_python-0.1.11/python/PYTHON_GUIDE.md +467 -0
  87. lme_python-0.1.11/python/lme_python.pyi +271 -0
  88. {lme_python-0.1.8 → lme_python-0.1.11}/python/src/lib.rs +736 -20
  89. {lme_python-0.1.8 → lme_python-0.1.11}/python/tests/test_api_parity.py +51 -0
  90. lme_python-0.1.11/python/tests/test_cv.py +82 -0
  91. lme_python-0.1.11/python/tests/test_dataframe_inputs.py +45 -0
  92. lme_python-0.1.11/python/tests/test_golden_parity.py +121 -0
  93. lme_python-0.1.11/python/tests/test_nlmer_custom_mean.py +54 -0
  94. lme_python-0.1.11/python/tests/test_simulate_scale.py +43 -0
  95. lme_python-0.1.11/python/tests/test_wheel_notices.py +86 -0
  96. lme_python-0.1.11/python/uv.lock +2289 -0
  97. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ci/README.md +2 -2
  98. lme_python-0.1.11/scripts/ci/check_legal_compliance.py +99 -0
  99. lme_python-0.1.11/scripts/ci/julia_format.jl +56 -0
  100. lme_python-0.1.11/scripts/ci/lme_ci.py +720 -0
  101. lme_python-0.1.11/scripts/ci/package_wheel_notices.py +165 -0
  102. lme_python-0.1.11/scripts/ci/r_format.R +19 -0
  103. lme_python-0.1.11/scripts/run_fair_rust_julia_benchmark.py +663 -0
  104. lme_python-0.1.11/scripts/run_perf_breakdown.py +342 -0
  105. lme_python-0.1.11/scripts/verify_cross_language_parity.py +331 -0
  106. {lme_python-0.1.8 → lme_python-0.1.11}/src/anova.rs +51 -0
  107. {lme_python-0.1.8 → lme_python-0.1.11}/src/anova_contrasts.rs +36 -2
  108. lme_python-0.1.11/src/bootstrap.rs +372 -0
  109. lme_python-0.1.11/src/cv.rs +413 -0
  110. {lme_python-0.1.8 → lme_python-0.1.11}/src/family.rs +194 -33
  111. {lme_python-0.1.8 → lme_python-0.1.11}/src/glmm_math.rs +15 -179
  112. lme_python-0.1.11/src/intercept_blocked.rs +1782 -0
  113. {lme_python-0.1.8 → lme_python-0.1.11}/src/lib.rs +374 -196
  114. lme_python-0.1.11/src/math.rs +2368 -0
  115. lme_python-0.1.11/src/model_matrix.rs +1295 -0
  116. lme_python-0.1.11/src/nlmm/fit.rs +1075 -0
  117. lme_python-0.1.11/src/nlmm/formula.rs +294 -0
  118. lme_python-0.1.11/src/nlmm/mean.rs +26 -0
  119. lme_python-0.1.11/src/nlmm/mean_fn.rs +156 -0
  120. lme_python-0.1.11/src/nlmm/mod.rs +90 -0
  121. lme_python-0.1.11/src/nlmm/predict.rs +104 -0
  122. lme_python-0.1.11/src/nlmm/re_cov.rs +125 -0
  123. lme_python-0.1.11/src/nlmm/self_start.rs +274 -0
  124. lme_python-0.1.11/src/nlmm/ssasymp.rs +33 -0
  125. lme_python-0.1.11/src/nlmm/ssgompertz.rs +47 -0
  126. lme_python-0.1.11/src/nlmm/ssmicmen.rs +27 -0
  127. lme_python-0.1.11/src/nlmm/sspower.rs +45 -0
  128. {lme_python-0.1.8 → lme_python-0.1.11}/src/optimizer.rs +344 -24
  129. lme_python-0.1.11/src/perf_diag.rs +302 -0
  130. lme_python-0.1.11/src/quadrature.rs +187 -0
  131. {lme_python-0.1.8 → lme_python-0.1.11}/src/satterthwaite.rs +2 -0
  132. lme_python-0.1.11/src/simulate.rs +197 -0
  133. lme_python-0.1.11/tests/data/cbpp_binary.csv +843 -0
  134. lme_python-0.1.11/tests/data/cbpp_binary_weighted.csv +843 -0
  135. lme_python-0.1.11/tests/data/glmm_binomial_probit.json +872 -0
  136. lme_python-0.1.11/tests/data/glmm_binomial_weighted.json +872 -0
  137. lme_python-0.1.11/tests/data/glmm_poisson_offset.json +536 -0
  138. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/golden_parity_manifest.json +413 -36
  139. lme_python-0.1.11/tests/data/grouseticks.csv +404 -0
  140. lme_python-0.1.11/tests/data/orange_nlmer.json +12 -0
  141. lme_python-0.1.11/tests/data/pastes_cask_reml.json +100 -0
  142. lme_python-0.1.11/tests/data/sleepstudy.csv +181 -0
  143. lme_python-0.1.11/tests/data/sleepstudy_offset_reml.json +246 -0
  144. lme_python-0.1.11/tests/data/ssasymp_synthetic.csv +61 -0
  145. lme_python-0.1.11/tests/data/ssfol_nlmer.json +10 -0
  146. lme_python-0.1.11/tests/data/ssgompertz_nlmer.json +10 -0
  147. lme_python-0.1.11/tests/data/ssgompertz_synthetic.csv +61 -0
  148. lme_python-0.1.11/tests/data/ssmicmen_nlmer.json +10 -0
  149. lme_python-0.1.11/tests/data/ssmicmen_synthetic.csv +61 -0
  150. lme_python-0.1.11/tests/data/sspower_nlmer.json +10 -0
  151. lme_python-0.1.11/tests/data/sspower_synthetic.csv +61 -0
  152. lme_python-0.1.11/tests/gen_nlmm_extras.R +54 -0
  153. lme_python-0.1.11/tests/generate_nlmm_fixtures.R +124 -0
  154. lme_python-0.1.11/tests/generate_test_data.R +979 -0
  155. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_anova.rs +2 -0
  156. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_anova_types.rs +18 -0
  157. lme_python-0.1.11/tests/test_bootstrap.rs +131 -0
  158. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_confint_simulate.rs +71 -0
  159. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_contrast.rs +4 -0
  160. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_coverage_gaps.rs +2 -0
  161. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_crossed_mock.rs +14 -0
  162. lme_python-0.1.11/tests/test_cv.rs +164 -0
  163. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_e2e_lmer.rs +1 -1
  164. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_errors.rs +20 -21
  165. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_failure_modes.rs +14 -9
  166. lme_python-0.1.11/tests/test_glmm_links.rs +78 -0
  167. lme_python-0.1.11/tests/test_glmm_offset_grouseticks.rs +43 -0
  168. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_golden_parity.rs +78 -12
  169. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_intercept_only.rs +4 -1
  170. lme_python-0.1.11/tests/test_nlmm_agq.rs +37 -0
  171. lme_python-0.1.11/tests/test_nlmm_custom_mean.rs +51 -0
  172. lme_python-0.1.11/tests/test_nlmm_orange.rs +154 -0
  173. lme_python-0.1.11/tests/test_nlmm_orange_multi_re.rs +92 -0
  174. lme_python-0.1.8/tests/test_nlmm_orange.rs → lme_python-0.1.11/tests/test_nlmm_self_start.rs +11 -22
  175. lme_python-0.1.11/tests/test_nlmm_ssasymp.rs +79 -0
  176. lme_python-0.1.11/tests/test_nlmm_ssfol.rs +98 -0
  177. lme_python-0.1.11/tests/test_nlmm_ssmicmen.rs +37 -0
  178. lme_python-0.1.11/tests/test_nlmm_sspower.rs +99 -0
  179. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_production_load.rs +60 -3
  180. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_sandwich_math.R +3 -3
  181. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_statistical_identities.rs +1 -1
  182. lme_python-0.1.8/.github/workflows/crate-publish-dry-run.yml +0 -28
  183. lme_python-0.1.8/BENCHMARKS.md +0 -183
  184. lme_python-0.1.8/CHANGELOG.md +0 -118
  185. lme_python-0.1.8/README.md +0 -103
  186. lme_python-0.1.8/REPO_COMPLETION_BY_AREA.md +0 -74
  187. lme_python-0.1.8/Taskfile.yml +0 -108
  188. lme_python-0.1.8/python/PYTHON_GUIDE.md +0 -293
  189. lme_python-0.1.8/python/lme_python.pyi +0 -150
  190. lme_python-0.1.8/python/requirements-ci.txt +0 -87
  191. lme_python-0.1.8/scripts/ci/lme_ci.py +0 -404
  192. lme_python-0.1.8/src/math.rs +0 -420
  193. lme_python-0.1.8/src/model_matrix.rs +0 -611
  194. lme_python-0.1.8/src/nlmm/fit.rs +0 -528
  195. lme_python-0.1.8/src/nlmm/formula.rs +0 -124
  196. lme_python-0.1.8/src/nlmm/mod.rs +0 -37
  197. lme_python-0.1.8/tests/data/cbpp_binary.csv +0 -843
  198. lme_python-0.1.8/tests/data/grouseticks.csv +0 -404
  199. lme_python-0.1.8/tests/data/orange_nlmer.json +0 -15
  200. lme_python-0.1.8/tests/data/pastes_cask_reml.json +0 -17
  201. lme_python-0.1.8/tests/data/sleepstudy.csv +0 -181
  202. lme_python-0.1.8/tests/generate_test_data.R +0 -506
  203. {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/fuzz-smoke.yml +0 -0
  204. {lme_python-0.1.8 → lme_python-0.1.11}/LICENSE +0 -0
  205. {lme_python-0.1.8 → lme_python-0.1.11}/benches/bench_load_production.rs +0 -0
  206. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.py +0 -0
  207. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.rs +0 -0
  208. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/comparison_chart.png +0 -0
  209. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.py +0 -0
  210. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.rs +0 -0
  211. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.jl +0 -0
  212. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.py +0 -0
  213. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.rs +0 -0
  214. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.py +0 -0
  215. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.rs +0 -0
  216. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.py +0 -0
  217. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.rs +0 -0
  218. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.R +0 -0
  219. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.py +0 -0
  220. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.rs +0 -0
  221. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.py +0 -0
  222. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.rs +0 -0
  223. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.R +0 -0
  224. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.py +0 -0
  225. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.rs +0 -0
  226. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.py +0 -0
  227. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.rs +0 -0
  228. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.R +0 -0
  229. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.py +0 -0
  230. {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.rs +0 -0
  231. {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/app.js +0 -0
  232. {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/index.html +0 -0
  233. {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/styles.css +0 -0
  234. {lme_python-0.1.8 → lme_python-0.1.11}/docs/index.html +0 -0
  235. {lme_python-0.1.8 → lme_python-0.1.11}/mise.toml +0 -0
  236. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/glmer_cbpp.py +0 -0
  237. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/glmer_grouseticks.py +0 -0
  238. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/lm_basics.py +0 -0
  239. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/lmer_sleepstudy.py +0 -0
  240. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/model_comparison.py +0 -0
  241. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/.gitignore +0 -0
  242. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/README.md +0 -0
  243. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/compare_plots.py +0 -0
  244. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/figure_specs.py +0 -0
  245. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/find_rscript.py +0 -0
  246. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/numeric_overlay.py +0 -0
  247. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/paths.py +0 -0
  248. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/plot_demo.py +0 -0
  249. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/plot_r.R +0 -0
  250. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/run_all.py +0 -0
  251. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/README.md +0 -0
  252. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/conftest.py +0 -0
  253. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/parity.py +0 -0
  254. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/paths.py +0 -0
  255. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/run.py +0 -0
  256. {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/test_parity.py +0 -0
  257. {lme_python-0.1.8 → lme_python-0.1.11}/python/tests/test_basic.py +0 -0
  258. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ast_explorations/test_fiasto.rs +0 -0
  259. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
  260. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/build_benchmark_site.py +0 -0
  261. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ci/run.sh +0 -0
  262. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/dump_dyestuff.R +0 -0
  263. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/dump_pastes.R +0 -0
  264. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/local_ci.ps1 +0 -0
  265. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/local_ci.sh +0 -0
  266. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/make_penicillin_csv.py +0 -0
  267. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/plot_comparisons.py +0 -0
  268. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/run_cross_language_benchmarks.py +0 -0
  269. {lme_python-0.1.8 → lme_python-0.1.11}/scripts/sync_github_repo_metadata.py +0 -0
  270. {lme_python-0.1.8 → lme_python-0.1.11}/src/contrast.rs +0 -0
  271. {lme_python-0.1.8 → lme_python-0.1.11}/src/ddf.rs +0 -0
  272. {lme_python-0.1.8 → lme_python-0.1.11}/src/formula.rs +0 -0
  273. {lme_python-0.1.8 → lme_python-0.1.11}/src/kenward_roger.rs +0 -0
  274. {lme_python-0.1.8 → lme_python-0.1.11}/src/kr_modcomp.rs +0 -0
  275. {lme_python-0.1.8 → lme_python-0.1.11}/src/kr_vcov_adj.rs +0 -0
  276. {lme_python-0.1.8 → lme_python-0.1.11}/src/nlmm/sslogis.rs +0 -0
  277. {lme_python-0.1.8 → lme_python-0.1.11}/src/robust.rs +0 -0
  278. {lme_python-0.1.8 → lme_python-0.1.11}/tests/categorical_anova_test.rs +0 -0
  279. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/dyestuff.csv +0 -0
  280. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/glmm_binomial.json +0 -0
  281. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/glmm_poisson.json +0 -0
  282. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/intercept_only.json +0 -0
  283. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/mock_crossed.json +0 -0
  284. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/mock_ml.json +0 -0
  285. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/orange.csv +0 -0
  286. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/pastes.csv +0 -0
  287. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/penicillin.csv +0 -0
  288. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/penicillin.json +0 -0
  289. {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/random_slopes.json +0 -0
  290. {lme_python-0.1.8 → lme_python-0.1.11}/tests/generate_mock_data.py +0 -0
  291. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_conditional_real.rs +0 -0
  292. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_coverage_edge_cases.rs +0 -0
  293. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_edge_cases.rs +0 -0
  294. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_formula.rs +0 -0
  295. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_formula_stress.rs +0 -0
  296. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_gaps.rs +0 -0
  297. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_glmm.rs +0 -0
  298. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_glmm_weighted.rs +0 -0
  299. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_kenward_roger.rs +0 -0
  300. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_kr_modcomp_pastes.rs +0 -0
  301. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_ml_optimization.rs +0 -0
  302. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_no_intercept.rs +0 -0
  303. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_numerical_parity.rs +0 -0
  304. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_predict.rs +0 -0
  305. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_random_slopes.rs +0 -0
  306. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_robust.rs +0 -0
  307. {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_satterthwaite.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,12 @@
1
1
  name: Security audit
2
2
 
3
+ # Automatic runs: v* release tags only. Use workflow_dispatch for an on-demand audit.
4
+
3
5
  on:
4
- schedule:
5
- # Weekly: Mondays 12:00 UTC
6
- - cron: "0 12 * * 1"
7
6
  workflow_dispatch:
8
7
  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"
8
+ tags:
9
+ - "v*"
22
10
 
23
11
  permissions:
24
12
  contents: read
@@ -49,25 +37,21 @@ jobs:
49
37
  run: cargo audit
50
38
 
51
39
  pip-audit:
52
- name: pip-audit (python lockfile)
40
+ name: pip-audit (uv.lock)
53
41
  runs-on: ubuntu-latest
54
42
  steps:
55
43
  - uses: actions/checkout@v6
56
44
 
57
- - name: Set up Python
58
- uses: actions/setup-python@v6
45
+ - name: Set up uv
46
+ uses: astral-sh/setup-uv@v8.3.2
59
47
  with:
60
48
  python-version: "3.11"
61
- cache: pip
62
- cache-dependency-path: python/requirements-ci.txt
49
+ enable-cache: true
50
+ cache-dependency-glob: "python/uv.lock"
63
51
 
64
- - name: Install locked CI dependencies
52
+ - name: pip-audit
65
53
  working-directory: python
66
54
  run: |
67
55
  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
56
+ uv sync --extra dev --no-install-project
57
+ 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
 
@@ -1,16 +1,13 @@
1
1
  name: CI
2
2
 
3
+ # Automatic runs: v* release tags only. Use workflow_dispatch for an intentional
4
+ # pre-release check on any branch (Actions → CI → Run workflow).
5
+
3
6
  on:
4
7
  push:
5
- branches: [master]
6
- paths-ignore:
7
- - "**/*.md"
8
- - "LICENSE"
9
- pull_request:
10
- branches: [master]
11
- paths-ignore:
12
- - "**/*.md"
13
- - "LICENSE"
8
+ tags:
9
+ - "v*"
10
+ workflow_dispatch:
14
11
 
15
12
  permissions:
16
13
  contents: read
@@ -77,35 +74,25 @@ jobs:
77
74
  if: matrix.os == 'macos-latest'
78
75
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
79
76
 
80
- - name: Build
81
- run: cargo build --verbose --locked
82
-
83
77
  - name: Run tests
84
78
  run: cargo test --verbose --locked
85
79
 
86
- - name: Set up Python
87
- uses: actions/setup-python@v6
80
+ - name: Set up uv
81
+ uses: astral-sh/setup-uv@v8.3.2
88
82
  with:
89
83
  python-version: "3.11"
90
- cache: pip
91
- cache-dependency-path: python/requirements-ci.txt
84
+ enable-cache: true
85
+ cache-dependency-glob: "python/uv.lock"
92
86
 
93
87
  - name: Python bindings (maturin develop, pytest, wheel install, pytest)
94
88
  working-directory: python
95
89
  shell: bash
96
90
  run: |
97
91
  set -euo pipefail
98
- python -m venv .venv
99
- if [ -f .venv/Scripts/python.exe ]; then
100
- VPY=.venv/Scripts/python.exe
101
- else
102
- VPY=.venv/bin/python
103
- fi
104
- "$VPY" -m pip install -U pip
105
- "$VPY" -m pip install -r requirements-ci.txt
106
- "$VPY" -m maturin develop --release
107
- "$VPY" -m pytest tests/ -v
108
- "$VPY" -m maturin build --release -o dist
92
+ uv sync --extra dev --no-install-project
93
+ uv run maturin develop --release
94
+ uv run pytest tests/ -v
95
+ uv run maturin build --release -o dist
109
96
  shopt -s nullglob
110
97
  wheels=(dist/lme_python-*.whl)
111
98
  if [ ${#wheels[@]} -eq 0 ]; then
@@ -113,8 +100,8 @@ jobs:
113
100
  ls -la dist 2>/dev/null || true
114
101
  exit 1
115
102
  fi
116
- "$VPY" -m pip install --force-reinstall "${wheels[0]}"
117
- "$VPY" -m pytest tests/ -v
103
+ uv run pip install --force-reinstall "${wheels[0]}"
104
+ uv run pytest tests/ -v
118
105
 
119
106
  rust-all-targets:
120
107
  name: Rust check (all targets)
@@ -205,28 +192,22 @@ jobs:
205
192
  target
206
193
  key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
207
194
  restore-keys: ubuntu-cargo-
208
- - name: Set up Python
209
- uses: actions/setup-python@v6
195
+ - name: Set up uv
196
+ uses: astral-sh/setup-uv@v8.3.2
210
197
  with:
211
198
  python-version: ${{ matrix.python-version }}
212
- cache: pip
213
- cache-dependency-path: python/requirements-ci.txt
199
+ enable-cache: true
200
+ cache-dependency-glob: "python/uv.lock"
201
+
214
202
  - name: Python bindings (maturin develop, pytest, wheel install, pytest)
215
203
  working-directory: python
216
204
  shell: bash
217
205
  run: |
218
206
  set -euo pipefail
219
- python -m venv .venv
220
- if [ -f .venv/Scripts/python.exe ]; then
221
- VPY=.venv/Scripts/python.exe
222
- else
223
- VPY=.venv/bin/python
224
- fi
225
- "$VPY" -m pip install -U pip
226
- "$VPY" -m pip install -r requirements-ci.txt
227
- "$VPY" -m maturin develop --release
228
- "$VPY" -m pytest tests/ -v
229
- "$VPY" -m maturin build --release -o dist
207
+ uv sync --extra dev --no-install-project
208
+ uv run maturin develop --release
209
+ uv run pytest tests/ -v
210
+ uv run maturin build --release -o dist
230
211
  shopt -s nullglob
231
212
  wheels=(dist/lme_python-*.whl)
232
213
  if [ ${#wheels[@]} -eq 0 ]; then
@@ -234,8 +215,8 @@ jobs:
234
215
  ls -la dist 2>/dev/null || true
235
216
  exit 1
236
217
  fi
237
- "$VPY" -m pip install --force-reinstall "${wheels[0]}"
238
- "$VPY" -m pytest tests/ -v
218
+ uv run pip install --force-reinstall "${wheels[0]}"
219
+ uv run pytest tests/ -v
239
220
 
240
221
  docs:
241
222
  name: Build docs
@@ -0,0 +1,43 @@
1
+ name: Publish Rust crate
2
+
3
+ # Publishes to crates.io only on v* tag pushes. Manual dispatch validates with
4
+ # cargo publish --dry-run (no registry token required).
5
+
6
+ on:
7
+ push:
8
+ tags:
9
+ - "v*"
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: publish-crate-${{ github.workflow }}-${{ github.ref }}
17
+ cancel-in-progress: true
18
+
19
+ jobs:
20
+ publish:
21
+ name: cargo publish
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - uses: actions/checkout@v6
25
+
26
+ - uses: dtolnay/rust-toolchain@stable
27
+
28
+ - name: Validate crate package (dry run)
29
+ if: github.event_name == 'workflow_dispatch'
30
+ run: cargo publish --dry-run --locked
31
+
32
+ - name: Publish root crate
33
+ if: startsWith(github.ref, 'refs/tags/')
34
+ env:
35
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
36
+ run: |
37
+ VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
38
+ if cargo search lme-rs --limit 1 2>/dev/null | grep -q "= \"$VERSION\""; then
39
+ echo "lme-rs $VERSION already on crates.io; skipping publish."
40
+ exit 0
41
+ fi
42
+ test -n "$CARGO_REGISTRY_TOKEN" || { echo "CARGO_REGISTRY_TOKEN repository secret is required to publish a new version" >&2; exit 1; }
43
+ cargo publish --locked
@@ -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:
@@ -27,6 +21,9 @@ jobs:
27
21
  - uses: actions/setup-python@v6
28
22
  with:
29
23
  python-version: '3.10'
24
+ - uses: dtolnay/rust-toolchain@stable
25
+ - name: Validate legal notices
26
+ run: python scripts/ci/check_legal_compliance.py
30
27
  - name: Build wheels
31
28
  uses: PyO3/maturin-action@v1
32
29
  with:
@@ -43,6 +40,11 @@ jobs:
43
40
  env:
44
41
  CFLAGS: "-std=gnu99"
45
42
  CFLAGS_aarch64_unknown_linux_gnu: "-std=gnu99"
43
+ - name: Add third-party notices to wheels
44
+ run: |
45
+ for wheel in dist/*.whl; do
46
+ python scripts/ci/package_wheel_notices.py "$wheel"
47
+ done
46
48
  - name: Upload wheels
47
49
  uses: actions/upload-artifact@v7
48
50
  with:
@@ -60,12 +62,17 @@ jobs:
60
62
  with:
61
63
  python-version: '3.10'
62
64
  architecture: ${{ matrix.target }}
65
+ - uses: dtolnay/rust-toolchain@stable
66
+ - name: Validate legal notices
67
+ run: python scripts/ci/check_legal_compliance.py
63
68
  - name: Build wheels
64
69
  uses: PyO3/maturin-action@v1
65
70
  with:
66
71
  target: ${{ matrix.target }}
67
72
  args: --release --out dist --manifest-path python/Cargo.toml
68
73
  sccache: 'true'
74
+ - name: Add third-party notices to wheels
75
+ run: Get-ChildItem dist/*.whl | ForEach-Object { python scripts/ci/package_wheel_notices.py $_.FullName }
69
76
  - name: Upload wheels
70
77
  uses: actions/upload-artifact@v7
71
78
  with:
@@ -82,6 +89,9 @@ jobs:
82
89
  - uses: actions/setup-python@v6
83
90
  with:
84
91
  python-version: '3.10'
92
+ - uses: dtolnay/rust-toolchain@stable
93
+ - name: Validate legal notices
94
+ run: python scripts/ci/check_legal_compliance.py
85
95
  - name: Set OpenBLAS target (Apple Silicon wheel)
86
96
  if: matrix.target == 'aarch64'
87
97
  run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
@@ -91,6 +101,11 @@ jobs:
91
101
  target: ${{ matrix.target }}
92
102
  args: --release --out dist --manifest-path python/Cargo.toml
93
103
  sccache: 'true'
104
+ - name: Add third-party notices to wheels
105
+ run: |
106
+ for wheel in dist/*.whl; do
107
+ python scripts/ci/package_wheel_notices.py "$wheel"
108
+ done
94
109
  - name: Upload wheels
95
110
  uses: actions/upload-artifact@v7
96
111
  with:
@@ -101,6 +116,9 @@ jobs:
101
116
  runs-on: ubuntu-latest
102
117
  steps:
103
118
  - uses: actions/checkout@v6
119
+ - uses: dtolnay/rust-toolchain@stable
120
+ - name: Validate legal notices
121
+ run: python scripts/ci/check_legal_compliance.py
104
122
  - name: Build sdist
105
123
  uses: PyO3/maturin-action@v1
106
124
  with:
@@ -123,12 +141,25 @@ jobs:
123
141
  id-token: write # IMPORTANT: this permission is mandatory for trusted publishing
124
142
  contents: write # needed to create GitHub Release
125
143
  steps:
144
+ - uses: actions/checkout@v6
145
+
146
+ - uses: actions/setup-python@v6
147
+ with:
148
+ python-version: "3.11"
149
+
126
150
  - uses: actions/download-artifact@v7
127
151
  with:
128
152
  path: dist
129
153
  pattern: wheels-*
130
154
  merge-multiple: true
131
155
 
156
+ - name: Normalize and verify wheel licenses
157
+ run: |
158
+ set -euo pipefail
159
+ for wheel in dist/*.whl; do
160
+ python scripts/ci/package_wheel_notices.py "$wheel"
161
+ done
162
+
132
163
  - name: Publish to PyPI
133
164
  uses: pypa/gh-action-pypi-publish@release/v1
134
165
 
@@ -1,12 +1,12 @@
1
1
  name: Sync Repository Metadata
2
2
 
3
+ # Automatic runs: v* release tags only. Use workflow_dispatch to sync the About
4
+ # box from Cargo.toml without tagging.
5
+
3
6
  on:
4
7
  push:
5
- branches: [master]
6
- paths:
7
- - Cargo.toml
8
- - scripts/sync_github_repo_metadata.py
9
- - .github/workflows/repo-metadata.yml
8
+ tags:
9
+ - "v*"
10
10
  workflow_dispatch:
11
11
 
12
12
  permissions:
@@ -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
@@ -38,7 +38,7 @@ flowchart TD
38
38
  | [`scripts/ci/lme_ci.py`](lme_ci.py) | One cross-platform implementation (stdlib Python 3.10+) |
39
39
  | [`lefthook.yml`](../lefthook.yml) | Declarative hooks, **parallel** pre-commit, `stage_fixed` auto-staging |
40
40
  | [`mise.toml`](../mise.toml) | Pins Rust, Python 3.11, uv, lefthook, task — reproducible dev env |
41
- | [`uv`](https://docs.astral.sh/uv/) | Fast venv creation (`uv venv --python 3.11`), no repo-root `.venv` backup dance |
41
+ | [`uv`](https://docs.astral.sh/uv/) | Locked Python env (`uv sync` + `uv.lock`); Ruff via `uv tool run` |
42
42
  | **Ruff** | `python/tests` + `python/examples` on `task lint`; staged auto-fix on commit |
43
43
  | **pre-push hook** | Automatic `preflight` (lint + `cargo check --all-targets` + `cargo audit` + repo metadata dry-run) |
44
44
 
@@ -65,8 +65,11 @@ task hooks:install
65
65
  |---|---|---|
66
66
  | `**/*.rs` | `fmt` then `clippy` | `stage_fixed: true` re-stages rustfmt fixes |
67
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 |
68
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 |
69
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) |
70
73
 
71
74
  Runs **in parallel** where safe (Ruff vs Rust fmt). Clippy waits on fmt (`priority`); manifest `check` runs after clippy (`priority` 3).
72
75
 
@@ -81,7 +84,8 @@ Runs **`preflight`** in parallel with itself as one command:
81
84
  1. `lint` — Rust fmt/clippy + Ruff
82
85
  2. `cargo check --workspace --all-targets --locked` — compile graph including tests/examples
83
86
  3. `cargo audit` — root crate + `python/` (same as GHA audit job, without `pip-audit`)
84
- 4. `repo-metadata` — dry-run from `Cargo.toml`; **`--verify-token`** when `REPO_ADMIN_TOKEN` is in the environment
87
+ 4. `legal` — fixture provenance plus third-party license metadata and bundled notices
88
+ 5. `repo-metadata` — dry-run from `Cargo.toml`; **`--verify-token`** when `REPO_ADMIN_TOKEN` is in the environment
85
89
 
86
90
  Install `cargo-audit` once: `cargo install cargo-audit` (pinned in GHA as 0.22.1).
87
91
 
@@ -96,18 +100,26 @@ If GHA reports `401 Bad credentials` on **Sync Repository Metadata**, rotate the
96
100
 
97
101
  Bypass: `git push --no-verify`.
98
102
 
99
- **macOS Apple Silicon BLAS** (`openblas-static` vs MKL) is only exercised on `macos-latest` in GHA — Windows/Linux preflights cannot catch that matrix cell. After changing `Cargo.toml` BLAS target tables or release workflows, run `task ci` or wait for the macOS CI job.
103
+ **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.
104
+
105
+ **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.
100
106
 
101
107
  ## 3. Before finishing work (manual)
102
108
 
109
+ 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.
110
+
103
111
  ### Quick path
104
112
 
105
113
  ```powershell
106
114
  task lint
115
+ task test:fast # unit tests only (~seconds after compile)
107
116
  task preflight # same as pre-push hook (without pip-audit)
108
117
  task audit # full security audit incl. pip-audit
118
+ task legal # provenance and third-party license records
109
119
  ```
110
120
 
121
+ Full integration tests: `task test` or `cargo test --locked` (uses `[profile.test] opt-level = 2` in [`Cargo.toml`](Cargo.toml)).
122
+
111
123
  Runs Rust (`fmt --check` + clippy) and Python Ruff on `python/tests` and `python/examples`.
112
124
 
113
125
  If you changed **Python bindings**:
@@ -150,7 +162,7 @@ python scripts/ci/lme_ci.py ci
150
162
  python scripts/ci/lme_ci.py ci --reuse-venv --skip-wheel-reinstall
151
163
  ```
152
164
 
153
- **CI-only:** multi-OS matrix, Python 3.10/3.12/3.13, production-load gates, `cargo audit` / `pip-audit`.
165
+ **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.
154
166
 
155
167
  ## `lme_ci.py` commands
156
168
 
@@ -160,9 +172,19 @@ python scripts/ci/lme_ci.py ci --reuse-venv --skip-wheel-reinstall
160
172
  | `lint` | Rust + Python static checks |
161
173
  | `preflight` | Pre-push: lint + check + cargo audit + repo metadata |
162
174
  | `audit` | cargo audit + pip-audit (GHA mirror) |
175
+ | `legal` | Third-party notices, fixture provenance, and Rust license metadata |
163
176
  | `repo-metadata` | Dry-run GitHub About sync; verify token if `REPO_ADMIN_TOKEN` set |
177
+ | `benchmarks-smoke` | Release examples + one Rust cross-language benchmark |
178
+ | `benchmarks-preflight` | `benchmarks-smoke` + optional `sleepstudy.R` when R/lme4 present |
179
+ | `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) |
180
+ | `perf-breakdown` | Rust `LME_PERF_DIAG` vs Julia `optsum.feval`; reports `prepare` / `fit_prepared` / cold `lmer` |
164
181
  | `rust-lint` | `fmt --check` + clippy |
165
182
  | `ruff-lint` | Ruff on `python/tests` + `python/examples` |
183
+ | `comparison-format-check` | Optional styler/JuliaFormatter check on `comparisons/` (+ `tests/*.R`); use `--required` in GHA |
184
+ | `r-format-staged --fix` | Staged R comparison/golden-parity scripts |
185
+ | `julia-format-staged --fix` | Staged Julia comparison scripts |
186
+ | `build-test` | `cargo test` (full suite) |
187
+ | `test-fast` | `cargo test --lib` only (quick unit tests) |
166
188
  | `rust-all` | Rust slice without Python bindings tests |
167
189
  | `python` | Maturin + pytest (+ wheel pass) |
168
190
  | `ruff-staged --fix` | Staged Python lint/format |