Repository navigation
Enumerated conformance table: derive (qtype × op × backend) proof obligations from the dispatch registry — the test half of #3418 #3945
Description
Downstream of #3421 Phase 1, and the other half of #3418. Alfredo's concept ticket asks for the data table (llama.cpp's ggml_type_traits[]); this asks for the test matrix derived from it, so that admitting a quant type automatically creates its proof obligations instead of an author remembering N sites.
#3418 already states the problem in exactly these terms:
"'does architecture X work' is not actually one question — it's
architectures × quant_types × backends × call_sites, and today most of that matrix is untested."
That sentence is the specification. What is missing is the thing that enumerates it.
Why now: the 0.69.1 release paid for its absence, measured
Every item below is from the night of 2026-09-23 on release/0.69.1-batch-2.
- The GPU-eligibility whitelist is recorded in NINE places.
b9435c6b6is titled "the whitelist is recorded in SIX places and I updated one" — and undercounted: it missedcrates/apr-cli/contracts/apr-model-capability-v1.yaml, theinclude_str!'d packaged mirror, which held the release branch red for hours.dtype.rsitself is the eighth. The ninth iscrates/aprender-serve/src/infer/tests_11.rs:357. - The ninth was invisible to three separate audits, and the reason generalises: it asserts
is_legacy_gguf_quant(30)(BF16), and no BF16 change had happened — so a whitelist record stays latent until the type it names changes, and a sweep that looks for breakage cannot find it. It will now redden the in-flight BF16 work at the worst moment. dtype.rscalls itself "Single source of truth for the GPU-eligibility whitelist" and contradicted itself — prose listed ten types and explicitly saidQ5_1(7) … gated to CPU; the expression permitted thirteen, including 7, 20 and 21. Wrong in both directions: a reader concluded IQ3_S still needed work (it did not) and that Q5_1 was unsupported (it was permitted). That comment is what produced a wrong type number in a kernel assignment.- A capability string conflates two crates. "IQ also fails the CPU dequant path" is true of
aprender-core(which is whytensor_contractcannot inspect those tensors) and false ofaprender-serve(which decodes all four). One string, two crates, and an estimate was wrong until someone checked per-crate. - Six-plus bespoke per-type contracts already exist —
q2k-dequant-parity-v1,q3k-dequant-correctness-v1,q5k-dequant-correctness-v1,bf16-dequant-v1,quant-roundtrip-fidelity-v1, … — each hand-written for one type. That is the duplication ARCH: quant-type dispatch is duplicated across ~30 files with no single source of truth — propose a dedicated consolidation release #3418 describes, in the contract layer rather than the code layer.
What competitors do, since #3418 already reaches for llama.cpp
| how the kernel matrix is produced | |
|---|---|
| llama.cpp | test-backend-ops auto-enumerates (op × quant type × shape), runs backend vs CPU, compares by NMSE against a threshold. Plus test-quantize-fns for round-trip bounds, and perplexity delta as the regression backstop |
| PyTorch | OpInfo — a declarative op registry; the generic suites (dtypes, devices, gradients) are derived from it rather than written per op |
| vLLM | pytest kernels vs PyTorch eager (rtol/atol), plus greedy-output equality vs HF transformers |
| TensorRT-LLM | per-kernel unit tests, with MMLU/perplexity accuracy as the acceptance gate |
test-backend-ops walks every type the backend claims and tests each. Structurally, findings 1–3 above cannot happen in that design.
Where we are already ahead, and must not lose it
Our kernel bar is planted faults proven RED first (gemv_entry_name_tests_3477.rs:116; the contract's "never moved on a kernel's existence alone"). test-backend-ops has no fault-injection step, and neither does vLLM's kernel suite. Our CRUX judge (crux_inference_judge.py, #3739) also refuses to pass vacuously — no comparator answered, control ALL_WRONG, no control declared — which most differential harnesses cannot do.
So this is not "adopt llama.cpp's testing". It is: keep the discrimination bar, replace the hand-picked matrix with an enumerated one.
Proposal
Once #3421 Phase 1 lands quant_type_traits(), derive a conformance table from it:
- Enumerate
(qtype × op × backend × shape)from the registry, not from a hand-written list. A type admitted to the whitelist without its conformance rows is a build failure. - Per-format tolerance, declared in the registry. This is a correction to our own current standard: we are claiming EXACT (0 ULP / 0.000e0). That is achievable for BF16, where widening is a pure 16-bit shift, and not generally achievable for GEMV, whose reduction order differs from the CPU's — which is precisely why llama.cpp uses NMSE thresholds. Each type should declare its bar, or we will eventually assert bit-exactness where it is physically unavailable and then either weaken the gate or block a correct kernel.
- Generate, do not maintain, the support table. Docs: publish a GPU-vs-CPU model architecture support table (no user-facing doc exists today) #3077 (Alfredo: publish a GPU-vs-CPU model architecture support table) becomes a rendering of the enumeration rather than a hand-kept document — and Docs: publish a GPU-vs-CPU model architecture support table (no user-facing doc exists today) #3077 stops being able to drift.
- Subsume the per-type contracts in finding 5 as rows, rather than leaving six hand-written siblings.
- Keep the mutation requirement per row, so an enumerated matrix cannot become a large vacuous one. An enumerated table that has never gone red is a bigger lie than a small one.
Precondition, and a correction to #3421
#3421 marks Phase 0 [x] complete and it is not. PR #3420 is OPEN, contracts/quant-dispatch-completeness-v1.yaml is not on main (it exists only on that branch, commit 1d7dcc5e9), and there are zero qdc_ tests in the tree. Verified on release/0.69.1-batch-2.
That is worth stating plainly because the five findings above are what #3418 predicted would keep happening, and its own foundation is sitting in an unmerged PR. Landing #3420 is the cheapest available step toward all of this.
Scope: 0.70.0 or the Inference dispatch & architecture consolidation milestone alongside #3423 — not the 0.69.1 train.
Activity
noahgift commentedon Sep 23, 2026
milestone → 0.74.0 per docs/roadmaps/milestone-triage-2026-09-23.md (#4024), operator approval 2026-09-23 (relayed by aprender-cf). Reason: qtype x op x backend obligations derived from dispatch: one quant dispatch (#4001)
noahgift commentedon Sep 23, 2026
Test half: #3968. Cop ruling (A) 2026-09-23: a census-driven device harness (every held (type, k, n), GPU vs CPU decoder, condition-aware bound, planted fault per type) plus a guard over (type, shape class). The guard lands with the harness's first green receipt on both hosts. Census: 163 held (type, k, n) across 17 whitelisted types; details in #3968.
feat(cuda): census-driven device conformance harness + the per-shape …
fix(cuda): Q6_K conformance judges against the Q8_1-quantized activat…
fix(cuda): conformance input is a golden-ratio sequence — a scaled in…
fix(cuda): the Q8_1 mirror dequantizes with the f16-stored scale, as …
feat(cuda): IQ3_S/IQ2_S are refused on cc >= 12 until #4096 — the whi…
noahgift commentedon Sep 25, 2026
Parked by APR-EPIC-001: over S2 budget (rank 37/38). Reopen = pull. Applied by aprender-d1: claude+agy both said close, apr shadow row recorded (handoff/close-quorum-2026-09-25.json).