Chapter 5 — Quantization from scratch
status: polished · path: Muse Glimmer, pinned Muser tree
Prerequisites: Chapters 1–4. You know why one decode token costs roughly “the whole model in bytes” (Chapter 1’s bytes-per-token spine), and you know that Metal kernels are compiled from one of three fingerprinted sources (Chapter 4). This chapter never launches a kernel — it is pure arithmetic on how a number becomes fewer bytes. The concrete formats arrive in Ch 6 and Ch 7.
Chapter 4 left you with a working compute pipeline: .metal source becomes a
runnable kernel through the concatenated fast-math library, the strict-f32
cross-vendor library, or the pinned llama.cpp metallib — and a fingerprint
line tells you which one actually ran. Ready to feed it work, we now face the
problem that dwarfs every kernel decision: the weights have to fit, and
they have to be read — every one of them, every token.
This chapter builds quantization from zero, with no allegiance to any particular format. We will invent a tiny 4-bit scheme, pack a block by hand, dequantize it with every multiply written out, and measure the error we just introduced. The real formats — kquant in Chapter 6, NVFP4 in Chapter 7 — are industrial versions of exactly this construction.
5.1 Why fp16 alone cannot carry a 30B model
Before we can argue about formats we have to know what we are carrying. Two questions decide everything that follows: how many weights are there, and how many bytes may each one cost? The first has an exact answer, and it is worth counting ourselves rather than trusting the nameplate on the box — every byte estimate in this chapter is built on that count, so an error here would quietly poison the whole argument.
A weight (also called a parameter) is one learned coefficient of the
model. Muse Glimmer is nominally a 30B-class model; counting the tensors the
loader actually validates gives a precise number. The engine’s config
asserts every tensor’s shape at load
[crates/muser-engine/src/config.rs:286], and the shapes are (Figure 5.1):
per layer (52 layers):
attn_q [hidden 6656 → 4096] attn_gate [6656 → 4096]
attn_k, attn_v [6656 → 256] attn_output[4096 → 6656]
ffn_gate, up [6656 → 19968] ffn_down [19968 → 6656]
embedding table [6656 → vocab 202,048] lm_head [6656 → 202,048]
Figure 5.1: The tensor inventory implied by assert_tensor_shapes
(config.rs:294-318). Dimensions are GGUF ne order, [in_dim, out_dim].
Multiply it out (Figure 5.2 — we show the arithmetic so you can re-derive it):
attention per layer : 3 × (6656×4096) + 2 × (6656×256) = 85,196,800
ffn per layer : 3 × (6656×19968) = 398,721,024
per layer total : 483,917,824
× 52 layers : 25,163,726,848
embedding + lm_head : 2 × (6656×202,048) = 2,689,662,976
TOTAL : 27,853,389,824 ≈ 27.85 B
Figure 5.2: Parameter count by hand. The “30B” nameplate is nominal; the counted total is ≈ 27.85 B. Norm vectors (~1.7 M values) are lost in the rounding here.
That count is the input to everything else in the chapter. Two walls stand between it and a working decode loop, and they fail in different ways: one asks whether the model fits, the other whether it can be read fast enough. Only the second one settles the format question, but the first is where most people’s intuition starts, so we take it first.
The capacity wall. f16 — a 16-bit IEEE float, the “half precision” format — is the smallest widely-used floating-point representation, at 2 bytes per weight:
27,853,389,824 × 2 B = 55,706,779,648 B ≈ 55.7 GB
The decode host is one Mac with an M3 Ultra and 96 GB of unified memory
([docs/memory-footprint.md] intro). Fifty-odd gigabytes against ninety-six
looks like room to spare, and that is exactly the trap: the weights never
occupy memory alone. Everything needed to serve one request has to live
beside them — the KV cache, the per-token
Key/Value attention memory of Chapter 1’s cost model
(Ch 22 owns it in depth), which for the release
configuration (four full-context slots at 131,072 tokens) is 7.306 GB; the
DFlash draft artifact 1,631,205,312 B; the vision projector
1,400,328,928 B; and ~0.99 GB of f32 batch-activation widths for prefill
[docs/memory-footprint.md].
That sum is already ≈ 67 GB, and it is a floor, not a total.
memory-footprint.md is explicit about its own incompleteness — “summing
artifact sizes with the KV formula is … only a lower bound” — because the
operating system, Metal’s pipelines and workspaces, and per-slot sampler
state all draw on the same 96 GB and none of them appear in the addition.
An fp16 model is not a plan; it is a hope.
The bandwidth wall — the one that settles it. Chapter 1 established that decode is ~99% reading weights: every token’s forward pass streams the whole model through the GPU. There is no cache trick and no clever ordering that lets a decode step skip a weight: if it is in the model, it goes across the bus, once per token. Quantization is how Muser attacks that stream.
The pinned kquant artifact is 16,756,681,056 bytes. We hold that figure
twice over — it is the size recorded in the artifact manifest
[docs/memory-footprint.md], and the identical constant is pinned in the
engine crate itself [crates/muser-engine/src/lib.rs:14]. Per weight, that
is:
16,756,681,056 B × 8 bits/B ÷ 27,853,389,824 weights ≈ 4.81 bits/weight
Versus fp16’s 16 bits: the artifact is 3.33× lighter per token. If
decode stayed bandwidth-bound at the same effective rate — the regime
Chapter 1 proved — an fp16 model would run at roughly 35.4 ÷ 3.33 ≈ 10.6
tok/s at best, versus the measured kquant 35.440 tok/s [claims #11].
(Derived ceiling from the bytes ratio, not a measurement.) Capacity might
survive an fp16 model on a lucky day. Bandwidth does not.
Quantization is the answer: store each weight in fewer than 16 (or 32) bits, accept a small, controlled error per weight, and buy a 3.3× reduction in the per-token byte stream. Everything else in Part II is the engineering of “small and controlled.”
5.2 Numbers as bits: f32 and f16
Fewer bits per weight, then. But which bits, and taken from where? To shrink a number we must first say what a number is in memory. An IEEE 754 float is a sign, an exponent, and a mantissa (fraction):
f32 (32 bits): [ sign:1 ][ exponent:8 ][ mantissa:23 ] ~7 decimal digits
f16 (16 bits): [ sign:1 ][ exponent:5 ][ mantissa:10 ] ~3 decimal digits
Figure 5.3: IEEE float layouts. The mantissa fixes the relative precision; the exponent fixes the dynamic range. f16 spans 2^-14 to 65504.
The mantissa is a binary fraction between 1 and 2 (for normal numbers), so an f16 weight with 10 mantissa bits is known to about one part in 1,024 — roughly three decimal digits. Real transformer weights live near zero, commonly within ±0.05, and f16 represents that range comfortably. What f16 cannot do is fit two of them in a byte. For that we leave floating point behind.
5.3 The codebook idea: a number as an index
Here is the single idea behind every format in this book. Instead of storing the weight’s value, store an integer index into a small table of allowed values — a codebook. Four bits select among 2⁴ = 16 possible values. Each index is a nibble — half a byte, values 0–15 — and two nibbles pack into one byte, which is where the storage win comes from: 0.5 bytes per weight.
Said the other way round: we stop recording what the weight is and start recording which of a short agreed list of values it sits nearest to. The list itself never goes over the bus, because sender and reader already know it. That substitution — value for index, memory for agreement — is the whole of quantization; everything after it is bookkeeping about how the list is chosen.
Two families of codebook exist, and Part II contains one of each:
- Integer (uniform) codebooks. The 16 values are evenly spaced: a
base value plus
index × step. Every kquant format (Chapter 6) is integer codebooks all the way down. - Float codebooks. The 16 values are themselves tiny floats, spread with relative (multiplicative) spacing. NVFP4’s e2m1 (Chapter 7) is exactly a 16-entry float table.
Dequantization is the act of turning an index
back into a value: look up the table (or compute base + index × step), and
out comes an approximation of the original weight. Quantization
is the inverse: pick the index whose value is closest to the original.
The gap between the original value and its reconstruction is the quantization error — the price of the whole enterprise. The rest of this chapter is about driving that error down without spending bytes.
5.4 One scale for everything: too crude
The first scheme everyone writes down is the one that needs no bookkeeping at all: one global step size for the whole tensor, chosen from the largest magnitude weight, shared by every weight in every row. Take it seriously for a moment, because on paper it looks unimprovable — a single stored number of overhead for hundreds of millions of weights, which rounds to no overhead at all.
So follow it through and see where it lands. Say a tensor’s weights span roughly ±0.5, so a symmetric 4-bit grid with 16 levels would use a step of 1.0/15 ≈ 0.067. Any weight is then replaced by the nearest multiple of 0.067. The arithmetic is sound; the assumption buried underneath it is not. Real weight blocks are much narrower than the global span — a few dozen neighboring weights typically cluster inside a band a tenth as wide. A global grid spends most of its 16 levels on values that never occur in that neighborhood, and the local error is needlessly large.
That is the lesson worth carrying out of the dead end: a codebook is worth only as much as its agreement with the values it is actually asked to encode. What you save on the header, you pay back with interest in error. We will watch the same failure play out concretely in §5.7 (the DC-offset problem), and it is why no format in this book uses a single global scale.
The fix is to quantize locally, not globally.
5.5 Blocks and scales
Split the tensor into small, contiguous blocks and give each block its own scale (a local step size). Now the 4 bits express a position inside the narrow band this block actually uses:
value ≈ scale × index (symmetric: one number per block)
value ≈ scale × index + min (asymmetric: two numbers per block)
A block of 32 weights might span only ±0.05, so its private scale is ~0.1/15 ≈ 0.0067 — ten times finer than the global grid above, from the same 4-bit index. Any slice of a smooth distribution spans less than the whole distribution; that is the entire trick, and it is the idea behind every “K-family” format in Chapter 6.
Turn it around once more, because this is the sentence the rest of Part II leans on: the narrower the range a scale has to cover, the more of your sixteen levels land where the data actually is. Bits do not become more precise by being more numerous. They become more precise by being asked a narrower question.
The scale itself is stored in floating point (typically f16, 2 bytes), because it must cover a wide range of magnitudes across blocks with only a few values of precision — the same division of labor as Figure 5.3, now between the scale (coarse, wide-range) and the index (fine, local).
5.6 Symmetric vs asymmetric: the min+offset trick
One stored number per block, or two? The choice looks like arithmetic and is really a question about what you are willing to assume about the data before you have looked at it.
Symmetric quantization assumes the
block’s values are roughly centered on zero. One number per block — the
scale — and the codebook spans −scale·max_index … +scale·max_index with 0
landing exactly on 0.
Asymmetric quantization adds a second number per block — the min (offset) — so the codebook can start wherever the data starts: `value = scale × index
- min`. This is the min+offset trick: it costs one extra stored number per block and buys correct handling of blocks that do not live around zero.
Symmetric (1 number/block) Asymmetric (2 numbers/block)
──────────────────────────── ────────────────────────────────
grid: −A ··· 0 ··· +A grid: min ··· min + 15·scale
assumes: zero-centered assumes: nothing
index 0 → −A (or 0) index 0 → min (offset!)
waste: shifted blocks lose waste: one extra f16 per block
half their levels
value = scale × index value = scale × index + min
Figure 5.4: The two codebook geometries. Q4_0 and Q8_0 (Chapter 6) are symmetric; Q4_K and Q5_K are asymmetric; NVFP4’s float codebook is symmetric by construction (its table is ± pairs).
When does the difference bite? When a block has a DC offset — a mean
far from zero. A block whose values all lie in, say, [0.30, 0.42] forces a
symmetric grid to cover ±0.42, and every negative level is wasted: only
about half the grid is ever addressed. The asymmetric grid puts min = 0.30
at index 0 and uses all 16 levels inside the 0.12-wide band — a resolution
roughly 3.5× finer for the same 4 bits.
There is an honest gap in that argument, and it matters for what we are allowed to claim later. Whether weight blocks in a given checkpoint actually carry enough offset to make the trick pay is an empirical property of the checkpoint, and it is not one we measured for this one [unverified for Muse Glimmer]. What we can say is that the format designers thought it worth the bytes: the release artifact pays for asymmetric blocks on most tensors, which is Chapter 6’s byte-level story.
5.7 The worked example: an 8-element block, 4 bits, every step
Everything so far has been argument. Now we do the thing itself, slowly, with nothing hidden — because a reader who has packed one block by hand can read any format in this book, and a reader who has not will be taking the byte layouts ahead on faith. This is the heart of the chapter — the template every later quant chapter reuses. We quantize one block by hand. The numbers below are schematic, chosen so the arithmetic is clean; they are not taken from any checkpoint. Watch two things as they go past: where the error ends up landing, which is not where most people guess, and how few bytes the block costs when we are done.
The block. Eight weights:
x = [ -0.15, 0.29, 0.12, -0.03, 0.20, 0.06, -0.09, 0.17 ]
Step 1 — scan the block.
min = −0.15 max = 0.29 range = max − min = 0.44
Step 2 — fit the scale. 4 bits give 16 levels, indices 0–15. We want
index 15 to land exactly on max:
scale = range / 15 = 0.44 / 15 = 0.029333…
For hand arithmetic, round the scale up to 0.03 — quantizers really do pick a convenient scale (Chapter 6’s scales are 6-bit integers times an f16; Chapter 7’s are powers-of-two floats), and a slightly padded range is safe: indices stay in 0–15 without clamping.
Step 3 — quantize each element. index_i = round((x_i − min) / scale):
x[0] = −0.15 → ( 0.00)/0.03 = 0.0 → 0
x[1] = 0.29 → ( 0.44)/0.03 = 14.67 → 15
x[2] = 0.12 → ( 0.27)/0.03 = 9.0 → 9
x[3] = −0.03 → ( 0.12)/0.03 = 4.0 → 4
x[4] = 0.20 → ( 0.35)/0.03 = 11.67 → 12
x[5] = 0.06 → ( 0.21)/0.03 = 7.0 → 7
x[6] = −0.09 → ( 0.06)/0.03 = 2.0 → 2
x[7] = 0.17 → ( 0.32)/0.03 = 10.67 → 11
Step 4 — pack. Eight indices, 4 bits each, two per byte, low nibble
first (the convention Chapter 6 will meet in real code —
q & 0x0F is the low element, q >> 4 the high one):
pairs : (0,15) (9,4) (12,7) (2,11)
bytes : 0x0F 0x49 0x7C 0x2B
block : [ min as f16 ][ scale as f16 ][ 0F 49 7C 2B ]
2 bytes 2 bytes 4 bytes → 8 bytes total
Step 5 — dequantize. x̂_i = min + scale × index_i. Every multiply
shown:
x̂[0] = −0.15 + 0.03 × 0 = −0.15 (exact)
x̂[1] = −0.15 + 0.03 × 15 = −0.15 + 0.45 = 0.30 (true 0.29)
x̂[2] = −0.15 + 0.03 × 9 = −0.15 + 0.27 = 0.12 (exact)
x̂[3] = −0.15 + 0.03 × 4 = −0.15 + 0.12 = −0.03 (exact)
x̂[4] = −0.15 + 0.03 × 12 = −0.15 + 0.36 = 0.21 (true 0.20)
x̂[5] = −0.15 + 0.03 × 7 = −0.15 + 0.21 = 0.06 (exact)
x̂[6] = −0.15 + 0.03 × 2 = −0.15 + 0.06 = −0.09 (exact)
x̂[7] = −0.15 + 0.03 × 11 = −0.15 + 0.33 = 0.18 (true 0.17)
Step 6 — measure the error. Per-element error x̂_i − x_i:
errors: 0, +0.01, 0, 0, +0.01, 0, 0, +0.01
max abs error = 0.01
mean abs error = 0.00375
Three observations that generalize far beyond this toy:
- The elements that defined the range are exact or nearly so. The block minimum reconstructed perfectly; the maximum was off by exactly one scale-step because we padded the scale. A quantizer’s error is worst for values in the middle of the range, never for the extremes that set it.
- Error is bounded by scale/2. Every true value lies within half a step
of a level, so
|error| ≤ scale/2by construction. Here 0.015; we measured ≤ 0.01. - Five of eight elements came out exact because they happened to sit on
the grid. Real weights don’t sit on grids; real mean error lands near
scale/4. The toy flatters us — keep that in mind when extrapolating.
Step 7 — the symmetric control. A worked example that only ever runs one way proves nothing, so before accepting the asymmetric header we send the same block through the other geometry and compare. Quantize it symmetrically: scale = amax/7, reading the 4 bits as signed indices −7…+7, amax = 0.29, so scale = 0.29/7 ≈ 0.0414 — coarser than 0.03 even before accounting for the wasted negative levels this almost-centered block barely uses. The extra stored number has already earned its keep — and it earned it on a block that was nearly centered to begin with, which is the weakest case we could have handed it. The offset here is mild (min = −0.15, max = 0.29); for a strongly one-signed block the symmetric penalty is the full factor-of-two of Figure 5.4.
5.8 What the error costs downstream
A hundredth here and a hundredth there — does any of it survive contact with the model, or does it wash out? The question matters because the answer decides how nervous to be, and the honest answer is: it does not wash out, but it also does not behave like noise.
A weight’s quantization error is not an isolated blemish — it is a deterministic perturbation of the model. Each dot product in the forward pass — the multiply-and-add pairing of two equal-length vectors, the atom under every weight matrix in this book — mixes in one error term per weight. Through 52 layers the perturbations compound multiplicatively (the drift argument Chapter 12 makes for normalization). Three honest statements about the cost:
- The error is fixed and knowable. Weights are quantized once, offline;
the dequantized value is the same every token. It is a permanent, exact
bias — not noise. That is what makes cross-engine parity possible at all:
Muser’s kquant lane reproduces llama.cpp’s numbers bit-for-bit on the
shared format precisely because the bytes and the arithmetic order are
pinned
[crates/muser-engine/src/quant/k_block.rs:169-177]. - Quality loss is real, but it is bounded by measurements rather than by
vibes. Muser does not assert that quantization is harmless; it budgets
the harm and then checks the budget. NVFP4-versus-kquant relative
perplexity and top-token disagreement are gated per depth and per content
class, and where one content class came in above its calibrated gate, the
number was published rather than buried: docs text at 65,536 tokens,
15.134% against a 13.339% calibrated gate, carried as part of the claim
[claims #10]. Chapter 7 tells that story. - You cannot compare formats by one number. The same 4-bit-class quantization is invisible in plain decode — the lanes land inside each other’s noise, as §5.9 will show — and decisive the moment the shape of the work changes: in batched speculative verification the same bytes produce a 6.81 tok/s no-go, the story Chapter 7 tells. Precision, in other words, does not have a price. It has a price per batch shape and per content class, which is why the gates are written to localize the cost instead of returning a single verdict.
5.9 Block size: the memory-vs-overhead dial
How big should a block be? It sounds like a tuning knob and is really a fork in the road, because the two ends of the dial fail for opposite reasons — and the formats waiting in the next two chapters are best read as two different answers to this one question.
The block header (min + scale, say 4 bytes as two f16s) is paid once per block. The bitrate — bits per weight — is:
bitrate = payload bits + header bits / block size
= 4 + 32 / N
Turn the dial (Figure 5.5):
block size N header/weight total bits/weight local range
───────────── ───────────── ───────────────── ───────────────
8 4.000 8.00 very narrow
32 1.000 5.00 narrow
256 0.125 4.125 moderate
1024 0.031 4.03 wide
Figure 5.5: The block-size dial. Smaller blocks buy finer local scales and lower error; bigger blocks amortize the header. (This table assumes the naive one-scale-one-min-per-block header of §5.7.)
Both directions fail. At N = 1,024 the header is nearly free, but a block that wide spans much of the tensor’s dynamic range and the local-scale advantage evaporates — you drift back toward the global grid of §5.4. At N = 8 the error is superb and you have doubled the storage. Every real format parks somewhere in between and then engineers the header down:
- Q4_K (Chapter 6): N = 256 with an asymmetric header, made cheap by a two-level hierarchy — one f16 super-scale and one f16 super-min for the whole 256, plus six-bit sub-scales per 32-element sub-block. Total header: 16 bytes per 256 weights = 0.5 bits/weight → 4.5 bits/weight.
- NVFP4 (Chapter 7): N = 16 with a symmetric float header of a single one-byte e4m3fn scale, plus one f32 per tensor. Total: 4 + 8/16 = 4.5 bits/weight — the same bitrate as Q4_K by entirely different means.
That coincidence is worth pausing on: two formats, two codebook families, two block sizes — and the same 4.5 bits. Format design is the art of spending a fixed half-bit of overhead (on top of the 4 payload bits) in different places. And 4.5-ish bits is also where the real artifact lands on average: the whole-GGUF figure computed in §5.1 was 4.81 bits/weight (the excess over 4.5 is the deliberately more precise tensors Chapter 6 identifies — Q6_K’s 6.5625 and Q5_K’s 5.5).
One more axis the table hides: who pays to use the format. A tiny block with a cheap codebook dequantizes with one multiply (good for a hot kernel); a 256-block with 6-bit bit-packed sub-scales costs real decode work per block (Chapter 6 shows the kernel-side machinery). Block size is a deal between storage, error, and kernel complexity — not just a storage number.
5.10 Tradeoffs
Asymmetric vs symmetric, measured in bytes. What does honesty about
offsets actually cost, in the only currency decode cares about? The
min+offset trick costs one extra stored number per block. At N = 32 with f16
headers that is
4 + 64/32 = 6 bits/weight symmetric vs 4 + 96/32 = 7 bits/weight
asymmetric — a 17% storage tax for offset robustness. Q4_K’s two-level
hierarchy is precisely the invention that recovers the tax: min+offset at
4.5 bits/weight, the same bitrate a plain symmetric 32-block would waste
(4 + 16/32 = 4.5). Chapter 6 walks the real bytes.
4 bits vs 16, measured in tokens. Does the shrinking actually buy
tokens, or does the decode side hand the savings straight back as unpacking
work? The lane throughputs answer it: kquant (≈4.81 bits/weight average)
35.440 tok/s and native NVFP4 (4.5 bits/weight) 35.491 tok/s, measured in
the same paired five-rep cell — parity within noise, never claimed
faster [claims #11]. The measured existence of two independent ~4.5-bit
artifacts running at parity with the f16-KV llama.cpp comparator is the
strongest statement this book can make that quantization, done at this rate,
does not tax decode throughput.
The scope of that cell matters as much as the figures do, so we carry it rather than round it away: a 66-token prefix, 32 teacher-forced tokens, F16 KV, an adjacent lease window, and a +0.1444% edge for NVFP4 — a margin no one should read as a win, and the reason the claim says parity and stops there. What 4 bits does tax — the batched speculative verify path — is a Chapter 7 measurement.
Why not 2 bits? The dial in the previous section has a lower end too,
and after a chapter spent shrinking things the fair question is why we
stopped where we stopped. Nothing in this chapter’s arithmetic forbids going
further — 2-bit codebooks exist in the wild. But at 4 levels per block the
quantization error approaches the size of the local scale itself, and §5.8’s
error-compounding has nowhere to hide: the error stops being a correction to
the weight and starts being most of the weight. Muser’s own gates localized
quality cost at 4-bit-class formats to specific content classes
[claims #10], and even that took a calibrated per-content gate to see at
all. So the ending here is an admission rather than a verdict: we do not
know what the next step down would have cost on this model, because no
2-bit lane was ever qualified in this program [unverified — no
measurement exists in the retained evidence].
5.11 What comes next
You now own the complete template: a codebook, a local scale, an optional
min, a hand-packed block, a dequant with every multiply visible, and an
error budget. Chapter 6 deploys it on the bytes Muser actually ships — the
kquant family (Q4_K, Q5_K, Q6_K) that fills the 16,756,681,056-byte
reference artifact, byte layouts first, then the pinned
kernel_mul_mv_q*_K_f32 Metal kernels that consume them and the per-tensor
map of which class of weight gets which format.
References
[crates/muser-engine/src/config.rs:286]—assert_tensor_shapes: every tensor and shape the loader validates (Figure 5.1’s source).[crates/muser-engine/src/lib.rs:14]— the pinned artifact byte size 16,756,681,056 asserted in the crate docs.[docs/memory-footprint.md]— 96 GB M3 Ultra host, KV formula and the 7.306 GB four-slot figure, artifact manifest (16,756,681,056 / 1,631,205,312 / 1,400,328,928 B), “lower bound” caution.[claims #11]—docs/launch-claims.mdrow 11: plain Mac NVFP4 35.491 tok/s vs adjacent kquant 35.440 tok/s at original scope, parity within noise; also the five-rep cell description via the ledger (P1.3).[claims #10]— native NVFP4 quality gates and the published docs@65,536 content-local sensitivity (15.134% vs 13.339%).[crates/muser-engine/src/quant/k_block.rs:169-177]—dot_q4_k_f32_llamadoc: the pinned llama.cpp accumulation-order contract for Q4_K.- Ch 6 — the kquant family: real byte layouts, the 6-bit scale packing, and the dispatch table.
- Ch 7 — NVFP4: the float codebook and the native lane.
- [ferrite-book Ch 5] — the ancestor’s Q4_K chapter, whose hand-built-superblock method this chapter ports (pedagogical lineage only; all numbers here are re-derived from the Muser tree).