lme-python 0.1.8__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.
- lme_python-0.1.9/.JuliaFormatter.toml +8 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/audit.yml +10 -28
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/benchmarks.yml +24 -4
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/ci.yml +24 -46
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/python-release.yml +2 -8
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/repo-metadata.yml +2 -5
- {lme_python-0.1.8 → lme_python-0.1.9}/.gitignore +1 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/AGENTS.md +22 -3
- lme_python-0.1.9/BENCHMARKS.md +386 -0
- lme_python-0.1.9/BENCHMARK_COVERAGE.md +106 -0
- lme_python-0.1.9/CHANGELOG.md +174 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/CONTRIBUTING.md +39 -24
- {lme_python-0.1.8 → lme_python-0.1.9}/Cargo.lock +29 -28
- {lme_python-0.1.8 → lme_python-0.1.9}/Cargo.toml +22 -2
- {lme_python-0.1.8 → lme_python-0.1.9}/GUIDE.md +52 -12
- lme_python-0.1.9/OPTIMIZATION.md +488 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/PKG-INFO +3 -1
- {lme_python-0.1.8 → lme_python-0.1.9}/README.md +16 -11
- {lme_python-0.1.8 → lme_python-0.1.9}/RELEASING.md +3 -11
- lme_python-0.1.9/REPO_COMPLETION_BY_AREA.md +102 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/Taskfile.yml +46 -0
- lme_python-0.1.9/USABILITY.md +178 -0
- lme_python-0.1.9/benchmarks/fair-rust-julia-reference-2026-07-04.json +97 -0
- lme_python-0.1.9/benchmarks/fair-rust-julia-reference-2026-07-06.json +99 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/COMPARISONS.md +5 -2
- lme_python-0.1.9/comparisons/bench_fair_julia_perf.jl +95 -0
- lme_python-0.1.9/comparisons/bench_fair_julia_timing.jl +135 -0
- lme_python-0.1.9/comparisons/bench_fair_rust_julia.rs +464 -0
- lme_python-0.1.9/comparisons/bench_perf_breakdown.rs +141 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/categorical_anova.R +3 -2
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/categorical_anova.jl +1 -1
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_cbpp.R +8 -5
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_cbpp.jl +9 -9
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_gaussian.R +3 -6
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_grouseticks.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_grouseticks.jl +11 -11
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmer_weighted.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmer_weighted.jl +2 -1
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_dyestuff.jl +2 -2
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_pastes.R +2 -1
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_pastes.jl +8 -8
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_penicillin.jl +10 -10
- lme_python-0.1.9/comparisons/parity/glmm_offset.R +21 -0
- lme_python-0.1.9/comparisons/parity/glmm_offset.jl +27 -0
- lme_python-0.1.9/comparisons/parity/glmm_probit.R +21 -0
- lme_python-0.1.9/comparisons/parity/glmm_probit.jl +27 -0
- lme_python-0.1.9/comparisons/parity/glmm_weighted.R +22 -0
- lme_python-0.1.9/comparisons/parity/glmm_weighted.jl +27 -0
- lme_python-0.1.9/comparisons/parity/parity_r_setup.R +6 -0
- lme_python-0.1.9/comparisons/parity/sleepstudy_offset.R +21 -0
- lme_python-0.1.9/comparisons/parity/sleepstudy_offset.jl +27 -0
- lme_python-0.1.9/comparisons/parity_export.rs +125 -0
- lme_python-0.1.9/comparisons/r_setup.R +3 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy.R +1 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy.jl +12 -12
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy_ml.jl +1 -1
- lme_python-0.1.9/examples/nlmm_fixture_dump.rs +40 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/lefthook.yml +14 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/pyproject.toml +5 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/Cargo.lock +23 -23
- {lme_python-0.1.8 → lme_python-0.1.9}/python/Cargo.toml +1 -1
- {lme_python-0.1.8 → lme_python-0.1.9}/python/PYTHON_GUIDE.md +94 -19
- {lme_python-0.1.8 → lme_python-0.1.9}/python/lme_python.pyi +38 -14
- {lme_python-0.1.8 → lme_python-0.1.9}/python/src/lib.rs +296 -14
- {lme_python-0.1.8 → lme_python-0.1.9}/python/tests/test_api_parity.py +51 -0
- lme_python-0.1.9/python/tests/test_dataframe_inputs.py +45 -0
- lme_python-0.1.9/python/tests/test_golden_parity.py +112 -0
- lme_python-0.1.9/python/tests/test_nlmer_custom_mean.py +54 -0
- lme_python-0.1.9/python/uv.lock +2289 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/ci/README.md +2 -2
- lme_python-0.1.9/scripts/ci/julia_format.jl +56 -0
- lme_python-0.1.9/scripts/ci/lme_ci.py +710 -0
- lme_python-0.1.9/scripts/ci/r_format.R +19 -0
- lme_python-0.1.9/scripts/run_fair_rust_julia_benchmark.py +655 -0
- lme_python-0.1.9/scripts/run_perf_breakdown.py +342 -0
- lme_python-0.1.9/scripts/verify_cross_language_parity.py +331 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/anova.rs +51 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/anova_contrasts.rs +36 -2
- {lme_python-0.1.8 → lme_python-0.1.9}/src/family.rs +194 -33
- {lme_python-0.1.8 → lme_python-0.1.9}/src/glmm_math.rs +15 -179
- lme_python-0.1.9/src/intercept_blocked.rs +1237 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/lib.rs +244 -137
- lme_python-0.1.9/src/math.rs +1699 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/model_matrix.rs +281 -63
- lme_python-0.1.9/src/nlmm/fit.rs +1075 -0
- lme_python-0.1.9/src/nlmm/formula.rs +284 -0
- lme_python-0.1.9/src/nlmm/mean.rs +25 -0
- lme_python-0.1.9/src/nlmm/mean_fn.rs +151 -0
- lme_python-0.1.9/src/nlmm/mod.rs +87 -0
- lme_python-0.1.9/src/nlmm/predict.rs +104 -0
- lme_python-0.1.9/src/nlmm/re_cov.rs +125 -0
- lme_python-0.1.9/src/nlmm/self_start.rs +214 -0
- lme_python-0.1.9/src/nlmm/ssasymp.rs +33 -0
- lme_python-0.1.9/src/nlmm/ssgompertz.rs +47 -0
- lme_python-0.1.9/src/nlmm/ssmicmen.rs +27 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/optimizer.rs +344 -24
- lme_python-0.1.9/src/perf_diag.rs +302 -0
- lme_python-0.1.9/src/quadrature.rs +187 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/satterthwaite.rs +2 -0
- lme_python-0.1.9/tests/data/cbpp_binary.csv +843 -0
- lme_python-0.1.9/tests/data/cbpp_binary_weighted.csv +843 -0
- lme_python-0.1.9/tests/data/glmm_binomial_probit.json +872 -0
- lme_python-0.1.9/tests/data/glmm_binomial_weighted.json +872 -0
- lme_python-0.1.9/tests/data/glmm_poisson_offset.json +536 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/golden_parity_manifest.json +374 -36
- lme_python-0.1.9/tests/data/grouseticks.csv +404 -0
- lme_python-0.1.9/tests/data/orange_nlmer.json +12 -0
- lme_python-0.1.9/tests/data/pastes_cask_reml.json +100 -0
- lme_python-0.1.9/tests/data/sleepstudy.csv +181 -0
- lme_python-0.1.9/tests/data/sleepstudy_offset_reml.json +246 -0
- lme_python-0.1.9/tests/data/ssasymp_synthetic.csv +61 -0
- lme_python-0.1.9/tests/data/ssfol_nlmer.json +10 -0
- lme_python-0.1.9/tests/data/ssgompertz_nlmer.json +10 -0
- lme_python-0.1.9/tests/data/ssgompertz_synthetic.csv +61 -0
- lme_python-0.1.9/tests/data/ssmicmen_nlmer.json +10 -0
- lme_python-0.1.9/tests/data/ssmicmen_synthetic.csv +61 -0
- lme_python-0.1.9/tests/gen_nlmm_extras.R +54 -0
- lme_python-0.1.9/tests/generate_nlmm_fixtures.R +65 -0
- lme_python-0.1.9/tests/generate_test_data.R +895 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_anova.rs +2 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_anova_types.rs +18 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_contrast.rs +4 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_coverage_gaps.rs +2 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_crossed_mock.rs +14 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_errors.rs +20 -21
- lme_python-0.1.9/tests/test_glmm_links.rs +78 -0
- lme_python-0.1.9/tests/test_glmm_offset_grouseticks.rs +43 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_golden_parity.rs +81 -12
- lme_python-0.1.9/tests/test_nlmm_agq.rs +37 -0
- lme_python-0.1.9/tests/test_nlmm_custom_mean.rs +51 -0
- lme_python-0.1.9/tests/test_nlmm_orange.rs +154 -0
- lme_python-0.1.9/tests/test_nlmm_orange_multi_re.rs +92 -0
- lme_python-0.1.8/tests/test_nlmm_orange.rs → lme_python-0.1.9/tests/test_nlmm_self_start.rs +11 -22
- lme_python-0.1.9/tests/test_nlmm_ssasymp.rs +79 -0
- lme_python-0.1.9/tests/test_nlmm_ssfol.rs +98 -0
- lme_python-0.1.9/tests/test_nlmm_ssmicmen.rs +37 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_production_load.rs +60 -3
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_sandwich_math.R +3 -3
- lme_python-0.1.8/BENCHMARKS.md +0 -183
- lme_python-0.1.8/CHANGELOG.md +0 -118
- lme_python-0.1.8/REPO_COMPLETION_BY_AREA.md +0 -74
- 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/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.9}/.github/workflows/crate-publish-dry-run.yml +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/.github/workflows/fuzz-smoke.yml +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/LICENSE +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/benches/bench_load_production.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/benches/bench_math.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/categorical_anova.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/categorical_anova.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/comparison_chart.png +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_cbpp.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_cbpp.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_gaussian.jl +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_gaussian.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_gaussian.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_grouseticks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/glmm_grouseticks.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmer_weighted.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmer_weighted.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_dyestuff.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_dyestuff.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_dyestuff.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_pastes.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_pastes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_penicillin.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_penicillin.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/lmm_penicillin.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy_ml.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy_ml.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/comparisons/sleepstudy_ml.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/docs/benchmarks/app.js +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/docs/benchmarks/index.html +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/docs/benchmarks/styles.css +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/docs/index.html +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/mise.toml +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/glmer_cbpp.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/glmer_grouseticks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/lm_basics.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/lmer_sleepstudy.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/model_comparison.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/.gitignore +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/README.md +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/compare_plots.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/figure_specs.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/find_rscript.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/numeric_overlay.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/paths.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/plot_demo.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/plot_r.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/plotting_demo/run_all.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/README.md +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/conftest.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/parity.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/paths.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/run.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/examples/verification_project/test_parity.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/python/tests/test_basic.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/ast_explorations/test_fiasto.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/ast_explorations/test_fiasto_offset.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/build_benchmark_site.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/ci/run.sh +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/dump_dyestuff.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/dump_pastes.R +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/local_ci.ps1 +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/local_ci.sh +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/make_penicillin_csv.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/plot_comparisons.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/run_cross_language_benchmarks.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/scripts/sync_github_repo_metadata.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/contrast.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/ddf.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/formula.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/kenward_roger.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/kr_modcomp.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/kr_vcov_adj.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/nlmm/sslogis.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/src/robust.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/categorical_anova_test.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/dyestuff.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/glmm_binomial.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/glmm_poisson.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/intercept_only.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/mock_crossed.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/mock_ml.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/orange.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/pastes.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/penicillin.csv +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/penicillin.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/data/random_slopes.json +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/generate_mock_data.py +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_conditional_real.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_confint_simulate.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_coverage_edge_cases.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_e2e_lmer.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_edge_cases.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_failure_modes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_formula.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_formula_stress.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_gaps.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_glmm.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_glmm_weighted.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_intercept_only.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_kenward_roger.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_kr_modcomp_pastes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_ml_optimization.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_no_intercept.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_numerical_parity.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_predict.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_random_slopes.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_robust.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_satterthwaite.rs +0 -0
- {lme_python-0.1.8 → lme_python-0.1.9}/tests/test_statistical_identities.rs +0 -0
|
@@ -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
|
-
|
|
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"
|
|
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 (
|
|
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
|
|
58
|
-
uses:
|
|
43
|
+
- name: Set up uv
|
|
44
|
+
uses: astral-sh/setup-uv@v6
|
|
59
45
|
with:
|
|
60
46
|
python-version: "3.11"
|
|
61
|
-
cache:
|
|
62
|
-
cache-dependency-
|
|
47
|
+
enable-cache: true
|
|
48
|
+
cache-dependency-glob: "python/uv.lock"
|
|
63
49
|
|
|
64
|
-
- name:
|
|
50
|
+
- name: pip-audit
|
|
65
51
|
working-directory: python
|
|
66
52
|
run: |
|
|
67
53
|
set -euo pipefail
|
|
68
|
-
|
|
69
|
-
|
|
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:
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
@@ -77,35 +71,25 @@ jobs:
|
|
|
77
71
|
if: matrix.os == 'macos-latest'
|
|
78
72
|
run: echo "OPENBLAS_TARGET=ARMV8" >> "$GITHUB_ENV"
|
|
79
73
|
|
|
80
|
-
- name: Build
|
|
81
|
-
run: cargo build --verbose --locked
|
|
82
|
-
|
|
83
74
|
- name: Run tests
|
|
84
75
|
run: cargo test --verbose --locked
|
|
85
76
|
|
|
86
|
-
- name: Set up
|
|
87
|
-
uses:
|
|
77
|
+
- name: Set up uv
|
|
78
|
+
uses: astral-sh/setup-uv@v6
|
|
88
79
|
with:
|
|
89
80
|
python-version: "3.11"
|
|
90
|
-
cache:
|
|
91
|
-
cache-dependency-
|
|
81
|
+
enable-cache: true
|
|
82
|
+
cache-dependency-glob: "python/uv.lock"
|
|
92
83
|
|
|
93
84
|
- name: Python bindings (maturin develop, pytest, wheel install, pytest)
|
|
94
85
|
working-directory: python
|
|
95
86
|
shell: bash
|
|
96
87
|
run: |
|
|
97
88
|
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
|
|
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
|
|
109
93
|
shopt -s nullglob
|
|
110
94
|
wheels=(dist/lme_python-*.whl)
|
|
111
95
|
if [ ${#wheels[@]} -eq 0 ]; then
|
|
@@ -113,8 +97,8 @@ jobs:
|
|
|
113
97
|
ls -la dist 2>/dev/null || true
|
|
114
98
|
exit 1
|
|
115
99
|
fi
|
|
116
|
-
|
|
117
|
-
|
|
100
|
+
uv run pip install --force-reinstall "${wheels[0]}"
|
|
101
|
+
uv run pytest tests/ -v
|
|
118
102
|
|
|
119
103
|
rust-all-targets:
|
|
120
104
|
name: Rust check (all targets)
|
|
@@ -205,28 +189,22 @@ jobs:
|
|
|
205
189
|
target
|
|
206
190
|
key: ubuntu-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
207
191
|
restore-keys: ubuntu-cargo-
|
|
208
|
-
- name: Set up
|
|
209
|
-
uses:
|
|
192
|
+
- name: Set up uv
|
|
193
|
+
uses: astral-sh/setup-uv@v6
|
|
210
194
|
with:
|
|
211
195
|
python-version: ${{ matrix.python-version }}
|
|
212
|
-
cache:
|
|
213
|
-
cache-dependency-
|
|
196
|
+
enable-cache: true
|
|
197
|
+
cache-dependency-glob: "python/uv.lock"
|
|
198
|
+
|
|
214
199
|
- name: Python bindings (maturin develop, pytest, wheel install, pytest)
|
|
215
200
|
working-directory: python
|
|
216
201
|
shell: bash
|
|
217
202
|
run: |
|
|
218
203
|
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
|
|
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
|
|
230
208
|
shopt -s nullglob
|
|
231
209
|
wheels=(dist/lme_python-*.whl)
|
|
232
210
|
if [ ${#wheels[@]} -eq 0 ]; then
|
|
@@ -234,8 +212,8 @@ jobs:
|
|
|
234
212
|
ls -la dist 2>/dev/null || true
|
|
235
213
|
exit 1
|
|
236
214
|
fi
|
|
237
|
-
|
|
238
|
-
|
|
215
|
+
uv run pip install --force-reinstall "${wheels[0]}"
|
|
216
|
+
uv run pytest tests/ -v
|
|
239
217
|
|
|
240
218
|
docs:
|
|
241
219
|
name: Build docs
|
|
@@ -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:
|
|
@@ -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
|
|
|
@@ -96,18 +99,25 @@ If GHA reports `401 Bad credentials` on **Sync Repository Metadata**, rotate the
|
|
|
96
99
|
|
|
97
100
|
Bypass: `git push --no-verify`.
|
|
98
101
|
|
|
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.
|
|
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.
|
|
100
105
|
|
|
101
106
|
## 3. Before finishing work (manual)
|
|
102
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
|
+
|
|
103
110
|
### Quick path
|
|
104
111
|
|
|
105
112
|
```powershell
|
|
106
113
|
task lint
|
|
114
|
+
task test:fast # unit tests only (~seconds after compile)
|
|
107
115
|
task preflight # same as pre-push hook (without pip-audit)
|
|
108
116
|
task audit # full security audit incl. pip-audit
|
|
109
117
|
```
|
|
110
118
|
|
|
119
|
+
Full integration tests: `task test` or `cargo test --locked` (uses `[profile.test] opt-level = 2` in [`Cargo.toml`](Cargo.toml)).
|
|
120
|
+
|
|
111
121
|
Runs Rust (`fmt --check` + clippy) and Python Ruff on `python/tests` and `python/examples`.
|
|
112
122
|
|
|
113
123
|
If you changed **Python bindings**:
|
|
@@ -150,7 +160,7 @@ python scripts/ci/lme_ci.py ci
|
|
|
150
160
|
python scripts/ci/lme_ci.py ci --reuse-venv --skip-wheel-reinstall
|
|
151
161
|
```
|
|
152
162
|
|
|
153
|
-
**
|
|
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.
|
|
154
164
|
|
|
155
165
|
## `lme_ci.py` commands
|
|
156
166
|
|
|
@@ -161,8 +171,17 @@ python scripts/ci/lme_ci.py ci --reuse-venv --skip-wheel-reinstall
|
|
|
161
171
|
| `preflight` | Pre-push: lint + check + cargo audit + repo metadata |
|
|
162
172
|
| `audit` | cargo audit + pip-audit (GHA mirror) |
|
|
163
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` |
|
|
164
178
|
| `rust-lint` | `fmt --check` + clippy |
|
|
165
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) |
|
|
166
185
|
| `rust-all` | Rust slice without Python bindings tests |
|
|
167
186
|
| `python` | Maturin + pytest (+ wheel pass) |
|
|
168
187
|
| `ruff-staged --fix` | Staged Python lint/format |
|