Regenerating the reference values
Part of the numerical-validation suite. The generator, the pinned reference environment, and what to do when a reference package changes.
The reference toolchain is needed only to author the values — CI just reads the committed JSON (so the suite runs everywhere the Dart suite runs, with no Python dependency on the CI machines). To regenerate after adding a procedure or changing a number:
python3 -m venv /tmp/refenv
/tmp/refenv/bin/pip install scipy statsmodels scikit-learn lifelines pingouin
/tmp/refenv/bin/python tool/validation/generate_references.py
dart run tool/cstool.dart test test/numerical_validation_test.dart
The package versions used for the committed values are recorded in the JSON
header (_versions) — at the time of writing SciPy 1.18, statsmodels 0.14,
scikit-learn 1.9, lifelines 0.30, pingouin 0.6, NumPy 2.5.
The survey-design, TOST, Bland–Altman, LCA, IRT,
LOESS, permutation, CFA follow-up and mediation follow-up references have their own toolchain — R with the survey package (the
canonical reference implementation for Taylor-linearized design-based
estimation; none of the pinned Python packages implements it), the
TOSTER package (the reference for equivalence testing), the blandr +
BlandAltmanLeh pair (the dual references for method comparison; blandr
also ships the original Bland & Altman 1986 PEFR data the fixture is
written from), the poLCA package (the reference for latent class
analysis; it ships the values and carcinoma example data the fixtures
are written from), the ltm + mirt pair (the dual references for
IRT; they ship the LSAT6/LSAT7 data), the coin + perm pair (the dual
references for permutation tests, with hand enumeration in R where
coin's exact algorithms do not reach), and — for the LOESS smoother —
base R alone (stats::loess is the reference; the cars fixture is a
base-R dataset), and — for the CFA follow-ups — lavaan (the reference
SEM package; it ships the Holzinger–Swineford data the fixture is written
from), and — for the mediation follow-ups — base R lm plus the
interactions package (the Johnson–Neyman reference). Like the Python
values, CI only reads the committed JSON:
# any R ≥ 4 with survey ≥ 4.4, TOSTER ≥ 0.8, blandr ≥ 0.6,
# BlandAltmanLeh ≥ 0.3, poLCA ≥ 1.6, ltm ≥ 1.2, mirt ≥ 1.4,
# coin ≥ 1.4 and perm ≥ 1.0; a user-space conda-forge R works:
# micromamba create -p ~/micromamba/envs/r-survey -c conda-forge \
# r-base r-survey r-toster r-polca r-coin r-lavaan \
# c-compiler cxx-compiler gfortran make
# # CRAN source builds need the env's bin on PATH for its compilers:
# PATH=~/micromamba/envs/r-survey/bin:$PATH Rscript -e \
# 'install.packages(c("blandr","BlandAltmanLeh","ltm","mirt","perm","interactions"),
# repos="https://cloud.r-project.org")'
Rscript tool/validation/generate_survey_references.R
Rscript tool/validation/generate_tost_references.R
Rscript tool/validation/generate_bland_altman_references.R
Rscript tool/validation/generate_lca_references.R
Rscript tool/validation/generate_irt_references.R
Rscript tool/validation/generate_loess_references.R
Rscript tool/validation/generate_permutation_references.R
Rscript tool/validation/generate_cfa_followups_references.R
Rscript tool/validation/generate_mediation_followups_references.R
dart run tool/cstool.dart test test/survey_validation_test.dart \
test/tost_validation_test.dart test/bland_altman_validation_test.dart \
test/lca_validation_test.dart test/irt_validation_test.dart \
test/loess_validation_test.dart test/permutation_validation_test.dart \
test/cfa_followups_validation_test.dart \
test/mediation_followups_validation_test.dart
The R and package versions used for the committed values are recorded in each
JSON header (_versions).
The statistics-package format artifacts have their own, smaller toolchain
(pyreadstat + pandas, and xlwt/odfpy for the spreadsheet fixtures):
python3 -m venv /tmp/fmtenv
/tmp/fmtenv/bin/pip install pyreadstat pandas
/tmp/fmtenv/bin/python tool/validation/generate_foreign_fixtures.py # create-if-absent
/tmp/fmtenv/bin/python tool/validation/generate_foreign_corpus.py # rewrites expected.json
dart run tool/cstool.dart test test/io_test.dart test/foreign_corpus_test.dart
The fixture writer is deliberately create-if-absent: .sav files embed a
creation timestamp, so an unconditional rewrite would churn committed bytes on
every run. Delete a fixture to genuinely regenerate it.
Adding a procedure
- In the generator, add a
add("<family>/<stat>", value, tol, "<source>")line computing the statistic with a reference package; re-run it. - In the Dart test, add a
Probe(or an entry to an existing one) mapping each newidto the engine request and the result cell that holds it. - Run the test. The orphan-guard will tell you immediately if the two sides are not in step.
When the reference disagrees, decide whether it is a real engine bug or a convention difference (a different-but-valid definition). If the latter, compute the reference in the engine's convention and record the choice in the Tolerances and recorded conventions list above — never just loosen the tolerance to make a genuine discrepancy pass.