fix(quantize): Q5_K readers decoded an invented layout — every Q5_K tensor on the CPU path and in apr import was wrong (PMAT-1101) - #3110
Closed
noahgift wants to merge 6 commits into
Closed
Conversation
…n the checkout is too shallow to hold it — intel passed on leftover history, every fresh yoga checkout died 'invalid object name 42be156' 42be156 is on main (the v1.5 spec's squash-merge, 2026-09-05) but deeper than CI's checkout; the long-lived intel workspaces carried it from earlier fetches, the ephemeral yoga runners never do, so guard-tree's 'PP-066 spec v1.6 defect table and its v1.5 RED proof' step exited 128 there (#3097 on yoga-build3). One object fetched by SHA when absent; the table and its RED proof are unchanged. Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… abbreviated one, so the shallow-checkout fetch fell through Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ensor on the CPU path and in apr import was wrong ggml's block_q5_K gives sub-blocks 2c and 2c+1 the same 32 qs bytes (low nibbles, then high nibbles) and takes the fifth bit of value l in sub-block s from bit s of qh[l]. realizar's dequantize_q5_k and fused_q5k_dot instead read the two nibbles of one byte as neighbouring values and took the fifth bit from qh[4s + l/8]. aprender-quant, the reader behind apr import, gave each sub-block its own 16 qs bytes, and its quantizer packed that way too. Measured on Qwen3.5-0.8B-Q4_K_M, whose attn_qkv and ssm_out are Q5_K: realizar disagreed with gguf-py on 5,959,751 of 6,291,456 values of blk.0.attn_qkv, and the layer-0 QKV sum was +26.64 against llama.cpp's -17.06. With this change it is -17.0967, and layers 0-2 of the #3091 trace match llama.cpp. Nothing caught it because every Q5_K test compared one aprender reader with another. realizar's dequantizer and fused dot now share a single block walker, so they cannot drift apart again. FALSIFY-QDOT-009 pins both readers to the 256 values that gguf-py (llama.cpp's own reader) produced from a block llama.cpp quantized. A round-trip on data whose sub-blocks all differ pins the packer to that reader; the old ramp round-trip gives every sub-block the same codes, and a packer that swapped sub-blocks survived it. Reverting either reader or the packer turns the tests red. The CUDA Q5_K gemv and gemm kernels carry two more wrong layouts. They get a separate PR. Refs #3091 Pmat-Ticket: PMAT-1101 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
…ckout) — guard-tree fails on every ephemeral runner without it Pmat-Ticket: PMAT-1101
This was referenced Sep 11, 2026
Draft
…cts gaps in falsification ids guard-cargo's "Contract corpus integrity" step (`cargo test -p aprender-contracts --test validate_contracts`) failed #3110 with "quantized-dot-product-v1: test ID gap: expected FALSIFY-QDOT-007, found FALSIFY-QDOT-009". I had skipped to 009 because crates/aprender-serve/src/quantize/contract_tests.rs already uses "FALSIFY-007" and "FALSIFY-QDOT-008". Those labels exist only in that file's panic strings. The corpus gate enforces the id sequence of the contract YAML itself, so the entry now takes the next id in the file, 007, everywhere it is cited. Pmat-Ticket: PMAT-1101 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
noahgift
added a commit
that referenced
this pull request
Sep 11, 2026
…ation ids Now that #3110's entry is FALSIFY-QDOT-007 (validate_contracts rejects gaps in the ids), the CUDA GEMV falsifier takes the next id, 008, and the shared fixture's header names 007/008. Pmat-Ticket: PMAT-1104 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Sep 11, 2026
github-merge-queue
Bot
removed this pull request from the merge queue due to a conflict with the base branch
Sep 11, 2026
noahgift
added a commit
that referenced
this pull request
Sep 11, 2026
…ark row, impossible since the type landed); pr-review-receipt pinned to X64 until reject-76-drop dies on arm64 (#3132) Train run 34648948737: workspace-test (intel-6) test_chat_completion_request_n_multiple set ChoiceCount::ONE and asserted '"n":5' — the deserializer refuses n!=1 by design, so the row could never pass; it was dark because aprender-serve's integration targets run only when the crate is touched (#3130) and the train touches it (#3110/#3113/#3099). The row now asserts the contract both ways (ONE serialises as 1; n=5 is refused with the client-visible message); 269/270 locally, the one local-only red (test_completion_request_with_all_params, f32-narrowed temperature vs Some(0.7)) does not reproduce in CI's feature-unified build and is not touched here. pr-review-receipt (gx10-pool1): mutate-guard.sh 232/233 — reject-76-drop survived on arm64 exactly as memory recorded for gx10-pool2; the job goes back to X64 with the issue named in the comment. Not a required check (ruleset: gate, workspace-test). Pmat-Ticket: PMAT-1098 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
This was referenced Sep 12, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Q5_K readers decoded an invented layout (PMAT-1101). P0 correctness, blocks #3091
What was wrong. ggml's
block_q5_Kstores 256 values in 176 bytes:d,dmin, 12 bytes of 6-bit scales and mins,qh[32], thenqs[128]. Sub-blocks2cand2c+1share the same 32qsbytes, low nibbles first and then high nibbles. The fifth bit of valuelin sub-blocksis bitsofqh[l]. Three aprender readers each did something different:dequantize_q5_k+fused_q5k_dotapr run/serve/chat)qh[4s + l/8]dequantize_q5_k_to_f32apr import(GGUF → APR)qsbytesquantize_q5_kMeasured on Qwen3.5-0.8B-Q4_K_M, where
attn_qkvandssm_outare Q5_K:blk.0.attn_qkv(6144×1024), realizar and gguf-py (llama.cpp's own Python reader) disagree on 5,959,751 of 6,291,456 values (max |Δ| 0.59).Why nothing caught it. Every Q5_K test compared one aprender reader against another. realizar's dequantizer and fused dot shared one invented layout; aprender-quant's quantizer and dequantizer shared another.
Fix
for_each_q5k_value, emits values in ggml order.dequantize_q5_kandfused_q5k_dotboth go through it, so the two can no longer disagree.FALSIFY-QDOT-007 (
contracts/quantized-dot-product-v1.yaml, both copies) pins the readers to the 256 values gguf-py produced from a real super-block that llama.cpp quantized. The fixture is embedded in the tests.quantize::tests::q5k_ggml::*: dequant is bit-exact; the fused dot with a one-hot activation returns each value exactly; the parallel matvec matches the gguf-py dot.tests::test_q5k_ggml_*: dequant is bit-exact; a quantize round-trip on data whose sub-blocks all differ stays within 0.15.Mutation check. Each change below was reverted locally, and the tests went red every time:
The packer mutant survived the existing ramp round-trip test, because a ramp gives every sub-block the same 5-bit codes. That is why the distinct-sub-block test exists.
Local gates
cargo fmt --check✓cargo test -p aprender-quant --lib✓cargo test -p aprender-serve --lib -- quantize::: 2223 passedquantize_coverage,property_quantize,gguf_quantize_coverage,quantize_property_tests)pv validate, both contract copies ✓cargo test -p aprender-contracts --lib✓check_baseline_ratchets.sh✓cargo deny check advisories✓guard_tree.sh --no-cargo✓, exceptcheck_complexity_ratchet.sh: the local pmat is not the pinned 3.39.0, so that failure is the instrument, not the code.cargo clippy -p aprender-quant --all-targets -- -D warnings -A unused-variables✓cargo clippy -p aprender-serve --lib --testsreports 5 errors that already exist on main, none in files this PR touches.Not in this PR: the CUDA Q5_K kernels in
aprender-gpu(q5k/gemv.rs,q5k/gemm.rs) carry two more wrong layouts. They are tracked in a separate issue and will get a GPU-side golden test.Refs #3091
Pmat-Ticket: PMAT-1101
🤖 Generated with Claude Code