-
Notifications
You must be signed in to change notification settings - Fork 0
Testing and Validation
Statistikles is a Kautz Type-1 neurosymbolic assistant: every number is computed by the Julia symbolic core, never by the LLM. That guarantee is only worth anything if the Julia core is itself correct, so the test suite is unusually large and is organised into layers — from smoke contracts, through mathematical property invariants, up to ground-truth reference validation where expected values are derived by hand or by a second, independent implementation (R / SciPy / Python) and never copied from the library's own output.
This page maps the whole suite as it actually exists in test/, explains how the reference
validation works, and shows how to run it.
The single entry point is test/runtests.jl.
It opens one big @testset "Statistikles Full Test Suite" and then includes seven extended
test files at the end:
# ── Extended test categories (CRG Grade C) ──────────────────────────────
include("e2e_test.jl") # full statistical pipelines
include("property_test.jl") # mathematical invariants over random inputs
include("reference_validation_test.jl") # trusted symbolic layer vs ground truth
include("degenerate_input_test.jl") # no NaN/Inf leaks; @assert -> ArgumentError
include("guardrail_test.jl") # the "no number from the LLM" boundary
include("executor_router_test.jl") # every LLM-facing tool exercised
include("reference_validation_advanced_test.jl")# KW, multiple/logistic regression, KM, metaCI runs the whole thing via Pkg.test:
julia --project=. -e 'using Pkg; Pkg.test(coverage=true)'The job that produces the merge gate is E2E — Julia Test Suite in
.github/workflows/e2e.yml.
| File | Focus | Test groups (@testset) |
|---|---|---|
runtests.jl (Full Suite) |
Every exported statistical function, TypeLL types, bridges, benchmarks, ecosystem integrations | ~100 |
e2e_test.jl |
End-to-end pipelines; graceful degradation on empty / single-element / NaN input | 10 |
property_test.jl |
Statistical identities validated over 100 random trials each | 14 |
reference_validation_test.jl |
Core layer checked against hand-derived / independently-computed ground truth | 14 |
degenerate_input_test.jl |
n = 1/2/3, constant, zero-variance boundaries return nothing, never NaN/Inf |
35 |
guardrail_test.jl |
The neural-boundary guarantee: numeric provenance auditing + executor guards | 8 |
executor_router_test.jl |
execute_tool argument coercion for every registered tool |
8 |
reference_validation_advanced_test.jl |
Ground truth for KW, multiple/logistic regression, Kaplan-Meier, meta-analysis | 7 |
chi_square_validation_test.jl |
Dedicated χ² review vs R 4.5.0 + SciPy 1.15.3 (standalone, see below) | 26 |
The property tests each run an explicit
for _ in 1:100loop with a fixed seed (Random.seed!(2026)), so a single "group" is actually 100 independent assertions with reproducible failures. Group counts above are structural markers, not an assertion total.
The in-file suite exercises essentially the whole src/stats/ surface (41 modules) plus the
type layer and the bridges. Representative sections:
-
Descriptive —
descriptive_stats,power_mean,weighted_stats(harmonic/geometric/ trimmed/winsorized/quadratic means, MAD, CV, skewness, kurtosis, quartiles). -
Inferential —
t_test_independent,one_way_anova,levenes_test. -
Nonparametric —
mann_whitney_u,wilcoxon_signed_rank,kruskal_wallis,friedman_test,cochrans_q,permanova/permanova_multi,dunn_test,ks_2sample,midranks,tie_correction. -
Correlation & regression —
pearson_correlation,spearman_correlation,partial_correlation,simple_linear_regression. -
Corrections —
adjust_p_values(bonferroni / holm / fdr / sidak). - Specialised — Fisher exact, McNemar, Grubbs, ICC, Bland-Altman, Anderson-Darling, MANOVA, Kaplan-Meier, meta-analysis, Mahalanobis, information theory (Shannon entropy, KL divergence).
- Non-classical — tropical algebra, Choquet integral, Bell/CHSH, modular / p-adic / GCD statistics, rough sets, Hurst exponent, degree centrality.
-
Type layer (TypeLL) —
Probability,EffectSize,TropicalValue,ModularInt, and theAuditSessionstate machine (compute → verify → prove → persist → report → complete). - BetLang — ternary primitives, uncertainty number systems, Latin hypercube / Sobol sampling, simulated annealing / particle swarm, financial risk (VaR / CVaR / Dutch-book / risk-of-ruin).
-
Cross-cutting "Aspect" tests — NaN handling, determinism, symmetry (
r(x,y) = r(y,x)), and mathematical invariants (QM ≥ AM ≥ GM ≥ HM,variance = std²,IQR = Q3 − Q1, CLR sums to zero, tropical idempotence). - Benchmarks — timing gates (e.g. descriptive stats < 100 ms on 10 000 points; Mann-Whitney < 200 ms on 1 000×1 000), so a performance regression fails the suite.
Full workflows from raw array to structured report, plus the error contract: empty, single-element,
and all-NaN inputs must return an error dict (haskey(result, "error")), never throw or emit
garbage. A NaN-containing array must process only the non-NaN observations
(result["n"] == count(!isnan, data)).
Twelve invariants, each over 100 random datasets — for example:
- mean of a constant array equals that constant; variance is 0;
-
min ≤ mean ≤ max;IQR = Q3 − Q1; - power-mean ordering
M₋₁ ≤ M₀ ≤ M₁ ≤ M₂; - CV is scale-invariant; uniform-weighted stats equal unweighted stats;
- Pearson
r ∈ [−1, 1]; every p-value∈ [0, 1].
Two invariants at the numerical boundary:
-
No NaN/Inf ever leaks into a returned
Dict. Undefined fields come back asnothing(JSONnull) plus an explanatory*_note, verified by a recursiveassert_finite_and_serialisablewalk over nested Dicts/Vectors. - Validation that used to be a disable-able
@assertnow throws a real, catchableArgumentError(a fullsrc/sweep —kl_divergence,centered_log_ratio,Probability,permanova,kaplan_meier,meta_analysis, and more).
Directly tests the flagship guarantee. It exercises collect_numbers (recursive numeric harvest
of tool results), extract_numeric_tokens (numeric literals in LLM prose), and
validate_numeric_provenance, which flags any number in the prose that does not trace to a
recorded tool result:
# INJECTED FABRICATION — 42.73 is legitimate; r = 0.87 and t = 3.14159 are fabricated.
fabricated = "The mean is 42.73 but the correlation was r = 0.87 and t = 3.14159."
ok, orphans = Statistikles.validate_numeric_provenance(fabricated, tool_results, Float64[])
@test !ok
@test "0.87" in orphans && "3.14159" in orphans
@test !("42.73" in orphans) # the one real number is not flaggedIt also covers process_tool_calls recovery from malformed tool calls (missing function,
invalid-JSON arguments) and the executor's clamps (n_reps, n_permutations, component-count
k ≤ 20) and unknown-sub-type dispatch guards. No HTTP or live LLM is needed — responses are
plain Dicts. See Experimental-Surfaces for the parts of the stack that are not enforced this
way.
execute_tool(name, args) is the exact seam the LLM drives: a bare tool-name string plus a
loosely-typed Dict. This file enumerates every tool from get_tools() (parsed, never
hand-copied) and asserts a clean (non-"error") Dict comes back for each:
registered_tools = [t["function"]["name"] for t in Statistikles.get_tools()]
for name in registered_tools
haskey(SKIP_LIST, name) && continue
@test haskey(ARG_TABLE, name)
result = Statistikles.execute_tool(name, ARG_TABLE[name])
@test result isa Dict && !haskey(result, "error")
endIf a tool is added to get_tools() without a matching ARG_TABLE/SKIP_LIST entry, the
"registry coverage" testset fails — coverage cannot silently rot. High-traffic tools are
additionally cross-checked against their direct Julia call to prove the router's argument coercion
doesn't change the numbers.
This is the layer that makes the no-mollocks guarantee meaningful. From the header of
reference_validation_test.jl:
The no-mollocks guarantee only holds if the Julia layer itself is correct. Every expected value below is hand-derived or independently computed … NOT copied from this library's own output. If one of these fails, the symbolic layer is producing wrong-but-deterministic numbers — the exact failure mode the project exists to prevent.
Three independent sources of truth are used, chosen so the check never degenerates into the library grading its own homework:
-
Closed-form / hand derivation. Inputs are engineered so the answer is exact. Example: a 2×3 table where every expected cell is exactly 4, giving
χ² = 4.0, anddf = 2uses the closed-form survival functionP(χ²₂ > x) = e^(−x/2)— independent of the library's ownDistributions.cdfcall path:observed = [6 2 4; 2 6 4] r = Statistikles.chi_square_test(observed) @test r["chi_squared"] == 4.0 @test r["df"] == 2 @test isapprox(r["p_value"], exp(-2.0); atol = 1e-12) @test isapprox(r["cramers_v"], sqrt(1 / 6); atol = 1e-12)
Other hand-derived checks: Welch t (
t = −1.897366…, Welch-Satterthwaitedf), Pearsonr = 12/√148, one-way ANOVA (F = 3, closed-formp = 0.125), Mann-Whitney complete separation (U = 0), and exact regression coefficients. -
Second independent implementation, run locally. The advanced file derives its constants with Python 3.13.5 + NumPy /
fractions.Fraction(exact rational arithmetic) for Kruskal-WallisH = 32/7, an orthogonal-design multiple regression (diagonalXᵀX), a saturated-binary logistic MLE (β = [−ln 3, 2 ln 3]), product-limit Kaplan-Meier survival, and fixed/random-effects (DerSimonian-Laird) meta-analysis. Crucially: "Python is used only to derive the numeric literals below; none of it enters the repository (governance bans Python in repo code)." -
χ² vs R and SciPy — see the dedicated file below.
Because the constants are external, a green run means the symbolic layer agrees with an independent authority to ~9–12 significant figures; a red run means the core is wrong, not the LLM.
A stand-alone correctness review of chi_square_test, chi_square_goodness_of_fit, and
frequency_table, checked against R 4.5.0's chisq.test and SciPy 1.15.3's
chi2_contingency / chisquare (both agreeing to ≥ 12 significant figures; only the constants,
not the derivation scripts, are committed). The header documents seven bugs this review found and
fixed, including:
- upper-tail p-value used
1 - cdf(...), which underflows to exactly0.0for large χ² — switched toccdf(...), accurate into the1e-22range; - missing guards for
r < 2/c < 2/ zero-observation tables (Chisq(0)DomainError); - a fully-zero row/column silently dropped while still charging its
df— now returnsnothingplus a"note", never a number; - a Yates continuity-correction term missing its
max(0.0, |O−E| − 0.5)²clamp, which inflated χ² near independence (buggy≈ 0.0256vs ground-truth0.0on[[10,10],[10,11]]).
This file carries its own using Test / using Statistikles and is run standalone (it is not
currently included by runtests.jl):
julia --project=. test/chi_square_validation_test.jlThe χ² ground-truth cases that do run inside the gated suite live in
reference_validation_test.jl (the Chi-square test of independence / goodness-of-fit
testsets).
Install dependencies first (the committed Manifest.toml pins everything):
julia --project=. -e 'using Pkg; Pkg.instantiate()'Then run the suite in any of these equivalent ways:
just test # via the Justfile recipe
julia --project=. test/runtests.jl # direct
julia --project=. -e 'using Pkg; Pkg.test()' # what CI runs (add coverage=true for lcov)Julia here lives in WSL, so invoke it through a login shell from the Windows side:
wsl.exe -d Debian -u hyperpolymath -- bash -lc \
'cd /path/to/statistikles && julia --project=. -e "using Pkg; Pkg.test()"'A -l (login) shell is needed so julia is on PATH.
e2e.yml runs Pkg.test(coverage=true), processes the result to lcov.info, and posts a
line-coverage summary to the job page. That summary is explicitly informational — not gated:
echo "## Coverage (informational — not gated)"
echo "Lines covered: **${HIT} / ${TOTAL}** (**${PCT}%**)"What is enforced:
- the
E2E — Julia Test Suitejob must pass for a PR to merge (branch protection); - a companion Aspect — Safety + SPDX job fails on dangerous patterns
(
believe_me,assert_total,sorry,Admitted), on anypostulateunderproofs/, and checks SPDX headers.
So the gate is "all tests green", with coverage tracked for visibility rather than as a hard threshold.
- Experimental-Surfaces — the honest status of the Zig FFI and Agda proofs (what is CI-checked vs placeholder / design-only / open target). The reference-validation layer above is what keeps the Julia core trustworthy; the experimental surfaces do not back it.
- Home
Overview
Using
Development
Project