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.
- lme_python-0.1.11/.JuliaFormatter.toml +8 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/audit.yml +12 -28
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/benchmarks.yml +24 -4
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/ci.yml +27 -46
- lme_python-0.1.11/.github/workflows/crate-publish-dry-run.yml +43 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/python-release.yml +39 -8
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/repo-metadata.yml +5 -5
- {lme_python-0.1.8 → lme_python-0.1.11}/.gitignore +1 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/AGENTS.md +26 -4
- lme_python-0.1.11/BENCHMARKS.md +460 -0
- lme_python-0.1.11/BENCHMARK_COVERAGE.md +109 -0
- lme_python-0.1.11/CHANGELOG.md +205 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/CONTRIBUTING.md +60 -24
- {lme_python-0.1.8 → lme_python-0.1.11}/Cargo.lock +31 -30
- {lme_python-0.1.8 → lme_python-0.1.11}/Cargo.toml +28 -3
- {lme_python-0.1.8 → lme_python-0.1.11}/GUIDE.md +194 -18
- lme_python-0.1.11/LICENSES/Apache-2.0.txt +193 -0
- lme_python-0.1.11/LICENSES/BSD-3-Clause-OpenBLAS.txt +31 -0
- lme_python-0.1.11/LICENSES/GPL-2.0-or-later.txt +342 -0
- lme_python-0.1.11/LICENSES/Intel-Simplified-Software-License.txt +79 -0
- lme_python-0.1.11/LICENSES/LGPL-2.1-only.txt +412 -0
- lme_python-0.1.11/OPTIMIZATION.md +530 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/PKG-INFO +3 -1
- lme_python-0.1.11/README.md +111 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/RELEASING.md +33 -46
- lme_python-0.1.11/RELINKING.md +22 -0
- lme_python-0.1.11/REPO_COMPLETION_BY_AREA.md +102 -0
- lme_python-0.1.11/THIRD_PARTY_NOTICES.md +18 -0
- lme_python-0.1.11/Taskfile.yml +194 -0
- lme_python-0.1.11/USABILITY.md +183 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/benches/bench_math.rs +3 -7
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-04.json +97 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-06.json +99 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-08.json +82 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-direct-intercept-gram.json +24 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-full-lmm.json +46 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-glmm.json +26 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-intercept-setup.json +26 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes-linear-cache.json +23 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-large-slopes.json +23 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09-sleepstudy-slopes.json +54 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-09.json +83 -0
- lme_python-0.1.11/benchmarks/fair-rust-julia-reference-2026-07-10-cold-setup-followup.json +24 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/COMPARISONS.md +18 -2
- lme_python-0.1.11/comparisons/bench_fair_julia_perf.jl +95 -0
- lme_python-0.1.11/comparisons/bench_fair_julia_timing.jl +135 -0
- lme_python-0.1.11/comparisons/bench_fair_rust_julia.rs +493 -0
- lme_python-0.1.11/comparisons/bench_perf_breakdown.rs +147 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.R +3 -2
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.jl +1 -1
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.R +8 -5
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.jl +9 -9
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.R +3 -6
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.jl +11 -11
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.jl +2 -1
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.jl +2 -2
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.R +2 -1
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.jl +8 -8
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.jl +10 -10
- lme_python-0.1.11/comparisons/nlmm_sspower.R +60 -0
- lme_python-0.1.11/comparisons/parity/glmm_offset.R +21 -0
- lme_python-0.1.11/comparisons/parity/glmm_offset.jl +27 -0
- lme_python-0.1.11/comparisons/parity/glmm_probit.R +21 -0
- lme_python-0.1.11/comparisons/parity/glmm_probit.jl +27 -0
- lme_python-0.1.11/comparisons/parity/glmm_weighted.R +22 -0
- lme_python-0.1.11/comparisons/parity/glmm_weighted.jl +27 -0
- lme_python-0.1.11/comparisons/parity/parity_r_setup.R +6 -0
- lme_python-0.1.11/comparisons/parity/sleepstudy_offset.R +21 -0
- lme_python-0.1.11/comparisons/parity/sleepstudy_offset.jl +27 -0
- lme_python-0.1.11/comparisons/parity_export.rs +120 -0
- lme_python-0.1.11/comparisons/r_setup.R +3 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.jl +12 -12
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.jl +1 -1
- lme_python-0.1.11/docs/CALO_CALIBRATION.md +124 -0
- lme_python-0.1.11/examples/batch_sspower_cpu.rs +221 -0
- lme_python-0.1.11/examples/nlmm_fixture_dump.rs +51 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/lefthook.yml +14 -0
- lme_python-0.1.11/legal/dependency-license-exceptions.json +17 -0
- lme_python-0.1.11/legal/fixture-provenance.json +40 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/pyproject.toml +5 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/Cargo.lock +26 -25
- {lme_python-0.1.8 → lme_python-0.1.11}/python/Cargo.toml +1 -1
- lme_python-0.1.11/python/PYTHON_GUIDE.md +467 -0
- lme_python-0.1.11/python/lme_python.pyi +271 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/src/lib.rs +736 -20
- {lme_python-0.1.8 → lme_python-0.1.11}/python/tests/test_api_parity.py +51 -0
- lme_python-0.1.11/python/tests/test_cv.py +82 -0
- lme_python-0.1.11/python/tests/test_dataframe_inputs.py +45 -0
- lme_python-0.1.11/python/tests/test_golden_parity.py +121 -0
- lme_python-0.1.11/python/tests/test_nlmer_custom_mean.py +54 -0
- lme_python-0.1.11/python/tests/test_simulate_scale.py +43 -0
- lme_python-0.1.11/python/tests/test_wheel_notices.py +86 -0
- lme_python-0.1.11/python/uv.lock +2289 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ci/README.md +2 -2
- lme_python-0.1.11/scripts/ci/check_legal_compliance.py +99 -0
- lme_python-0.1.11/scripts/ci/julia_format.jl +56 -0
- lme_python-0.1.11/scripts/ci/lme_ci.py +720 -0
- lme_python-0.1.11/scripts/ci/package_wheel_notices.py +165 -0
- lme_python-0.1.11/scripts/ci/r_format.R +19 -0
- lme_python-0.1.11/scripts/run_fair_rust_julia_benchmark.py +663 -0
- lme_python-0.1.11/scripts/run_perf_breakdown.py +342 -0
- lme_python-0.1.11/scripts/verify_cross_language_parity.py +331 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/anova.rs +51 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/anova_contrasts.rs +36 -2
- lme_python-0.1.11/src/bootstrap.rs +372 -0
- lme_python-0.1.11/src/cv.rs +413 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/family.rs +194 -33
- {lme_python-0.1.8 → lme_python-0.1.11}/src/glmm_math.rs +15 -179
- lme_python-0.1.11/src/intercept_blocked.rs +1782 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/lib.rs +374 -196
- lme_python-0.1.11/src/math.rs +2368 -0
- lme_python-0.1.11/src/model_matrix.rs +1295 -0
- lme_python-0.1.11/src/nlmm/fit.rs +1075 -0
- lme_python-0.1.11/src/nlmm/formula.rs +294 -0
- lme_python-0.1.11/src/nlmm/mean.rs +26 -0
- lme_python-0.1.11/src/nlmm/mean_fn.rs +156 -0
- lme_python-0.1.11/src/nlmm/mod.rs +90 -0
- lme_python-0.1.11/src/nlmm/predict.rs +104 -0
- lme_python-0.1.11/src/nlmm/re_cov.rs +125 -0
- lme_python-0.1.11/src/nlmm/self_start.rs +274 -0
- lme_python-0.1.11/src/nlmm/ssasymp.rs +33 -0
- lme_python-0.1.11/src/nlmm/ssgompertz.rs +47 -0
- lme_python-0.1.11/src/nlmm/ssmicmen.rs +27 -0
- lme_python-0.1.11/src/nlmm/sspower.rs +45 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/optimizer.rs +344 -24
- lme_python-0.1.11/src/perf_diag.rs +302 -0
- lme_python-0.1.11/src/quadrature.rs +187 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/satterthwaite.rs +2 -0
- lme_python-0.1.11/src/simulate.rs +197 -0
- lme_python-0.1.11/tests/data/cbpp_binary.csv +843 -0
- lme_python-0.1.11/tests/data/cbpp_binary_weighted.csv +843 -0
- lme_python-0.1.11/tests/data/glmm_binomial_probit.json +872 -0
- lme_python-0.1.11/tests/data/glmm_binomial_weighted.json +872 -0
- lme_python-0.1.11/tests/data/glmm_poisson_offset.json +536 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/golden_parity_manifest.json +413 -36
- lme_python-0.1.11/tests/data/grouseticks.csv +404 -0
- lme_python-0.1.11/tests/data/orange_nlmer.json +12 -0
- lme_python-0.1.11/tests/data/pastes_cask_reml.json +100 -0
- lme_python-0.1.11/tests/data/sleepstudy.csv +181 -0
- lme_python-0.1.11/tests/data/sleepstudy_offset_reml.json +246 -0
- lme_python-0.1.11/tests/data/ssasymp_synthetic.csv +61 -0
- lme_python-0.1.11/tests/data/ssfol_nlmer.json +10 -0
- lme_python-0.1.11/tests/data/ssgompertz_nlmer.json +10 -0
- lme_python-0.1.11/tests/data/ssgompertz_synthetic.csv +61 -0
- lme_python-0.1.11/tests/data/ssmicmen_nlmer.json +10 -0
- lme_python-0.1.11/tests/data/ssmicmen_synthetic.csv +61 -0
- lme_python-0.1.11/tests/data/sspower_nlmer.json +10 -0
- lme_python-0.1.11/tests/data/sspower_synthetic.csv +61 -0
- lme_python-0.1.11/tests/gen_nlmm_extras.R +54 -0
- lme_python-0.1.11/tests/generate_nlmm_fixtures.R +124 -0
- lme_python-0.1.11/tests/generate_test_data.R +979 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_anova.rs +2 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_anova_types.rs +18 -0
- lme_python-0.1.11/tests/test_bootstrap.rs +131 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_confint_simulate.rs +71 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_contrast.rs +4 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_coverage_gaps.rs +2 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_crossed_mock.rs +14 -0
- lme_python-0.1.11/tests/test_cv.rs +164 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_e2e_lmer.rs +1 -1
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_errors.rs +20 -21
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_failure_modes.rs +14 -9
- lme_python-0.1.11/tests/test_glmm_links.rs +78 -0
- lme_python-0.1.11/tests/test_glmm_offset_grouseticks.rs +43 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_golden_parity.rs +78 -12
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_intercept_only.rs +4 -1
- lme_python-0.1.11/tests/test_nlmm_agq.rs +37 -0
- lme_python-0.1.11/tests/test_nlmm_custom_mean.rs +51 -0
- lme_python-0.1.11/tests/test_nlmm_orange.rs +154 -0
- lme_python-0.1.11/tests/test_nlmm_orange_multi_re.rs +92 -0
- lme_python-0.1.8/tests/test_nlmm_orange.rs → lme_python-0.1.11/tests/test_nlmm_self_start.rs +11 -22
- lme_python-0.1.11/tests/test_nlmm_ssasymp.rs +79 -0
- lme_python-0.1.11/tests/test_nlmm_ssfol.rs +98 -0
- lme_python-0.1.11/tests/test_nlmm_ssmicmen.rs +37 -0
- lme_python-0.1.11/tests/test_nlmm_sspower.rs +99 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_production_load.rs +60 -3
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_sandwich_math.R +3 -3
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_statistical_identities.rs +1 -1
- lme_python-0.1.8/.github/workflows/crate-publish-dry-run.yml +0 -28
- lme_python-0.1.8/BENCHMARKS.md +0 -183
- lme_python-0.1.8/CHANGELOG.md +0 -118
- lme_python-0.1.8/README.md +0 -103
- lme_python-0.1.8/REPO_COMPLETION_BY_AREA.md +0 -74
- lme_python-0.1.8/Taskfile.yml +0 -108
- lme_python-0.1.8/python/PYTHON_GUIDE.md +0 -293
- lme_python-0.1.8/python/lme_python.pyi +0 -150
- lme_python-0.1.8/python/requirements-ci.txt +0 -87
- lme_python-0.1.8/scripts/ci/lme_ci.py +0 -404
- lme_python-0.1.8/src/math.rs +0 -420
- lme_python-0.1.8/src/model_matrix.rs +0 -611
- lme_python-0.1.8/src/nlmm/fit.rs +0 -528
- lme_python-0.1.8/src/nlmm/formula.rs +0 -124
- lme_python-0.1.8/src/nlmm/mod.rs +0 -37
- lme_python-0.1.8/tests/data/cbpp_binary.csv +0 -843
- lme_python-0.1.8/tests/data/grouseticks.csv +0 -404
- lme_python-0.1.8/tests/data/orange_nlmer.json +0 -15
- lme_python-0.1.8/tests/data/pastes_cask_reml.json +0 -17
- lme_python-0.1.8/tests/data/sleepstudy.csv +0 -181
- lme_python-0.1.8/tests/generate_test_data.R +0 -506
- {lme_python-0.1.8 → lme_python-0.1.11}/.github/workflows/fuzz-smoke.yml +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/LICENSE +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/benches/bench_load_production.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/categorical_anova.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/comparison_chart.png +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_cbpp.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.jl +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_gaussian.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/glmm_grouseticks.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmer_weighted.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_dyestuff.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_pastes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/lmm_penicillin.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/comparisons/sleepstudy_ml.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/app.js +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/index.html +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/docs/benchmarks/styles.css +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/docs/index.html +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/mise.toml +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/glmer_cbpp.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/glmer_grouseticks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/lm_basics.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/lmer_sleepstudy.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/model_comparison.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/.gitignore +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/README.md +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/compare_plots.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/figure_specs.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/find_rscript.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/numeric_overlay.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/paths.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/plot_demo.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/plot_r.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/plotting_demo/run_all.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/README.md +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/conftest.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/parity.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/paths.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/run.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/examples/verification_project/test_parity.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/python/tests/test_basic.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ast_explorations/test_fiasto.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/build_benchmark_site.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/ci/run.sh +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/dump_dyestuff.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/dump_pastes.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/local_ci.ps1 +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/local_ci.sh +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/make_penicillin_csv.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/plot_comparisons.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/run_cross_language_benchmarks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/scripts/sync_github_repo_metadata.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/contrast.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/ddf.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/formula.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/kenward_roger.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/kr_modcomp.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/kr_vcov_adj.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/nlmm/sslogis.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/src/robust.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/categorical_anova_test.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/dyestuff.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/glmm_binomial.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/glmm_poisson.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/intercept_only.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/mock_crossed.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/mock_ml.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/orange.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/pastes.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/penicillin.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/penicillin.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/data/random_slopes.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/generate_mock_data.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_conditional_real.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_coverage_edge_cases.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_edge_cases.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_formula.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_formula_stress.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_gaps.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_glmm.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_glmm_weighted.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_kenward_roger.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_kr_modcomp_pastes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_ml_optimization.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_no_intercept.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_numerical_parity.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_predict.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_random_slopes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_robust.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.11}/tests/test_satterthwaite.rs +0 -0
|
@@ -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
|
-
|
|
10
|
-
|
|
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 (
|
|
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
|
|
58
|
-
uses:
|
|
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:
|
|
62
|
-
cache-dependency-
|
|
49
|
+
enable-cache: true
|
|
50
|
+
cache-dependency-glob: "python/uv.lock"
|
|
63
51
|
|
|
64
|
-
- name:
|
|
52
|
+
- name: pip-audit
|
|
65
53
|
working-directory: python
|
|
66
54
|
run: |
|
|
67
55
|
set -euo pipefail
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
87
|
-
uses:
|
|
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:
|
|
91
|
-
cache-dependency-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
|
209
|
-
uses:
|
|
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:
|
|
213
|
-
cache-dependency-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
-
|
|
238
|
-
|
|
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
|
|
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
|
-
-
|
|
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
|
-
|
|
6
|
-
|
|
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:
|
|
@@ -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/) |
|
|
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. `
|
|
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.
|
|
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
|
-
**
|
|
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 |
|