spark_model/layers/dense_ffn_m16_tc_oracle.rs
1// SPDX-License-Identifier: AGPL-3.0-only
2
3//! The numerics contract for the tensor-core decode tiers (`w8a16_gemm_m16`,
4//! #927; `dense_gemm_m16_bf16`, #927/#928) — ONE comparison, evaluated by the
5//! GPU oracles (`examples/native_fp8_ffn_m16_tc_microtest.rs`,
6//! `examples/native_bf16_lm_head_m16_microtest.rs`) and by the host simulations
7//! (`dense_ffn_m16_tc_m32_tests.rs`, `ops/dense_gemm_m16_bf16_tests.rs`,
8//! `ops/dense_gemm_m16_bf16_floor_tests.rs`), so a
9//! receipt and a unit test cannot drift into grading different things.
10//!
11//! The kernel REASSOCIATES the K reduction relative to the scalar GEMV (an
12//! m16n8k16 MMA reduces 16 K-products in the tensor core's own order before
13//! they reach the FP32 accumulator), so the contract is a tolerance and always
14//! was. What changed in round 6 is WHICH tolerance; what changed in round 9 is
15//! how the tolerance's absolute half is SCALED — see [`m16_tc_acc_floor`].
16
17/// BF16 ordinal-ULP budget this tier is held to, against the scalar GEMV.
18/// Unchanged since #927.
19pub const M16_TC_MAX_ULP: i32 = 2;
20
21/// FP32 unit roundoff, `2^-24`. Every rounding the accumulator performs is a
22/// multiple of this times the magnitude being rounded, so it is the only
23/// constant in [`m16_tc_acc_floor`] that is not a judgement call.
24pub const F32_UNIT_ROUNDOFF: f64 = 5.960_464_477_539_063e-8;
25
26/// Margin over the `u * sqrt(K)` accumulation scale that [`m16_tc_acc_floor`]
27/// admits.
28///
29/// 🔴 CALIBRATED, NOT DERIVED, and the round-9 fix turns on it. The SHAPE of
30/// the floor (`u * sqrt(K) * row_rms`) follows from the arithmetic; the O(1)
31/// constant in front of it does not, because the m16n8k16's internal 16-product
32/// order is unspecified hardware and the H100 turns out to be noisier than any
33/// host model of it. So the constant is fixed by a receipt at both ends:
34///
35/// * **Above the noise.** H100 round 9 (`native_bf16_lm_head_m16_microtest`,
36/// 1xH100, 2026-09-11, the real `[N=248077, K=5120]` BF16 head) rejected a
37/// worst cell of `reference=-1.173019409e-4` at `max_ulp=100` against a block
38/// RMS of 23.80. One hundred ordinal BF16 ULP at that magnitude is an
39/// absolute error of 4.8e-5..9.1e-5. `8 * u * sqrt(5120) * 23.80` =
40/// **8.1e-4**, i.e. 9x above the worst error the receipt exhibits. (A host
41/// run of both reduction orders at the real shape reproduces that reference
42/// value bit-for-bit at row 7, column 228,041 — an INTERIOR column of CTA
43/// 7,126 of 7,753, nowhere near the 13-column tail. The reference arm is
44/// `dense_gemv_bf16`, which the host reproduces exactly, so the element's
45/// identity carries even though its ULP distance does not.)
46/// * **Below anything structural.** The same floor is 1/29,000 of that block's
47/// own RMS, and 28x below the microtest's weakest negative control (a
48/// three-ordinal mutation on a `|value| > 1`, which moves it by 0.0234). A
49/// row, pitch, offset or tail-mask defect misplaces whole outputs and lands
50/// errors of order the RMS itself — four orders above the floor.
51///
52/// A future widening has to argue with assertions, not with this comment:
53/// `the_accumulation_floor_still_rejects_a_structural_error` in
54/// `dense_ffn_m16_tc_m32_tests.rs`, and
55/// `the_round9_lm_head_outlier_is_a_cancelled_logit` plus
56/// `the_k_aware_floor_still_rejects_every_structural_error` in
57/// `ops/dense_gemm_m16_bf16_floor_tests.rs`.
58pub const M16_TC_ACC_FLOOR_MARGIN: f64 = 8.0;
59
60/// Absolute error below which an ordinal-ULP budget says nothing, for one
61/// output of a length-`k` FP32 reduction whose row has RMS `row_rms`.
62///
63/// 🔴 THIS IS THE ROUND-9 `lm_head` FIX, and it replaces round 6's fixed
64/// `2^-20 * block_rms`. Two things were wrong with that constant:
65///
66/// 1. **It did not scale with the reduction.** An ordinal BF16 ULP is a
67/// RELATIVE unit, and an output that has catastrophically cancelled has no
68/// relative accuracy left to measure — but the absolute noise it is measured
69/// against is a property of the REDUCTION, not of the output. Two FP32
70/// orders over the same `k` terms differ by a random walk of `k` roundings,
71/// i.e. `~u * sqrt(k)` times the scale of the terms; the terms' scale is
72/// `row_rms / sqrt(k)` when they are zero-mean and independent, so the
73/// difference scales as `u * sqrt(k) * row_rms`. `2^-20` is `u * 16`, so it
74/// happens to be the right size only near `k = 256`.
75/// 2. **It was fitted to ONE kernel pair.** Round 6 chose `2^-20` as "~3.5x
76/// above the worst error observed" on `w8a16_gemm_m16` vs `w8a16_gemv`,
77/// where BOTH arms fold a 128-wide FP8 scale block onto an outer FP32
78/// accumulator — a two-level reduction. `dense_gemm_m16_bf16` has no block
79/// scale, so its accumulator is ONE uninterrupted 320-step chain at
80/// K=5120 and is several times noisier. Round 9 measured the consequence:
81/// `over_budget` 10/16/25/37 at M=5/8/13/16 on the LM head — exactly linear
82/// in M, i.e. ~2 rejected elements per row out of 248,077 columns, the
83/// signature of a per-element statistical tail and not of a defect.
84///
85/// `row_rms` is the RMS of the REFERENCE row, not of the whole block: an
86/// element's accumulation noise is proportional to `||a_m||`, the norm of the
87/// activation row that produced it, and the row's output RMS is the only
88/// observable that tracks `||a_m||`. A block-wide RMS under-scales the floor
89/// for a hot row and over-scales it for a quiet one; the LM head's rows are
90/// different tokens, so that is a real difference in service even though the
91/// microtest's uniform fixture cannot show it.
92///
93/// The row's MAX `|ref|` was considered and rejected: it is a single order
94/// statistic, so it moves with the block width and with one outlier, whereas
95/// the reduction's noise depends on the row's L2 norm. RMS is that norm.
96pub fn m16_tc_acc_floor(k: usize, row_rms: f64) -> f64 {
97 M16_TC_ACC_FLOOR_MARGIN * F32_UNIT_ROUNDOFF * (k as f64).sqrt() * row_rms
98}
99
100/// BF16 bits -> a monotone integer, so `|ord(a) - ord(b)|` is the ULP distance
101/// and +0/-0 are the same point.
102pub fn bf16_ord(bits: u16) -> i32 {
103 if bits & 0x8000 != 0 {
104 -((bits & 0x7FFF) as i32)
105 } else {
106 bits as i32
107 }
108}
109
110/// The tier's numerics contract, as ONE predicate every oracle and host
111/// simulation evaluates, so they cannot drift.
112///
113/// An element passes if EITHER it is within [`M16_TC_MAX_ULP`] ordinal BF16 ULP
114/// of the reference, OR its absolute error is under [`m16_tc_acc_floor`] for
115/// the reduction depth `k` and the reference ROW's RMS.
116///
117/// `k` is the reduction depth the kernel actually ran, not the output width —
118/// passing `n` here would make the floor grow with the vocabulary, which is the
119/// one axis it must not depend on.
120pub fn within_m16_tc_budget(actual_bits: u16, reference_bits: u16, k: usize, row_rms: f64) -> bool {
121 if (bf16_ord(actual_bits) - bf16_ord(reference_bits)).abs() <= M16_TC_MAX_ULP {
122 return true;
123 }
124 let a = f64::from(half::bf16::from_bits(actual_bits).to_f32());
125 let b = f64::from(half::bf16::from_bits(reference_bits).to_f32());
126 (a - b).abs() <= m16_tc_acc_floor(k, row_rms)
127}
128
129/// One element the comparison rejected, with everything needed to say WHERE it
130/// is and WHY it failed.
131///
132/// Round 6 reported `over_budget 5` and nothing else, so the five elements
133/// could not be located without re-running the H100 — which is why the
134/// diagnosis took a host simulation. The report now names them, and round 9
135/// added `row_rms` because the floor is per-row: without it the printed
136/// `|ref|/rms` cannot be checked against the floor that actually rejected the
137/// element.
138#[derive(Debug, Clone, Copy)]
139pub struct M16TcOutlier {
140 pub row: usize,
141 pub col: usize,
142 pub reference: f32,
143 pub actual: f32,
144 pub ulp: i32,
145 /// RMS of the reference ROW this element sits in — the scale
146 /// [`m16_tc_acc_floor`] was evaluated at.
147 pub row_rms: f64,
148}
149
150/// The result of comparing an `m x n` BF16 block against the scalar reference.
151#[derive(Debug, Clone, Default)]
152pub struct M16TcDiff {
153 /// Largest ordinal BF16 ULP distance, sign flips excluded and counted.
154 pub max_ulp: i32,
155 /// Elements the FULL criterion rejected — ordinal budget AND the
156 /// accumulation floor. This is the number that gates a cell.
157 pub over_budget: Vec<M16TcOutlier>,
158 /// Elements the ordinal budget alone would have rejected. Reported so a
159 /// round-6-style cell can be read at a glance as "cancellation tail" rather
160 /// than investigated as a defect.
161 pub over_ulp_only: usize,
162 pub sign_flips: usize,
163 pub max_abs: f64,
164 pub rel_rms: f64,
165 /// RMS of the whole REFERENCE block. The floor is per-ROW, but this is the
166 /// number the receipts print and the one that makes an outlier's magnitude
167 /// interpretable at a glance.
168 pub rms: f64,
169 /// Per-row reference RMS, in row order — the scales the floor was actually
170 /// evaluated at. Printed by the oracles next to each outlier.
171 pub row_rms: Vec<f64>,
172}
173
174/// Magnitude below which a SIGN change carries no information: one ULP across
175/// zero is a full sign flip, so those are counted separately rather than
176/// graded. Unchanged from #927.
177pub const M16_TC_SIGN_FLIP_BAND: f64 = 0.05;
178
179/// RMS of a BF16 slice, in the f64 the floor is expressed in.
180fn bf16_rms(block: &[u8]) -> f64 {
181 let count = block.len() / 2;
182 if count == 0 {
183 return 0.0;
184 }
185 let sum: f64 = block
186 .chunks_exact(2)
187 .map(|b| {
188 let v = f64::from(half::bf16::from_bits(u16::from_le_bytes([b[0], b[1]])).to_f32());
189 v * v
190 })
191 .sum();
192 (sum / count as f64).sqrt()
193}
194
195/// Compare an `m x n` BF16 block against the scalar reference under the tier's
196/// contract. Both slices are `m * n` little-endian BF16 elements; `k` is the
197/// reduction depth the kernel ran.
198///
199/// Two passes, because the absolute floor is expressed against the reference
200/// row RMS and no row's RMS is known until it has been walked once.
201pub fn compare_m16_tc_block(actual: &[u8], reference: &[u8], n: usize, k: usize) -> M16TcDiff {
202 let bits = |b: &[u8]| u16::from_le_bytes([b[0], b[1]]);
203 let val = |b: u16| f64::from(half::bf16::from_bits(b).to_f32());
204 let row_bytes = n * 2;
205 let row_rms: Vec<f64> = if row_bytes == 0 {
206 Vec::new()
207 } else {
208 reference.chunks(row_bytes).map(bf16_rms).collect()
209 };
210 let mut d = M16TcDiff {
211 rms: bf16_rms(reference),
212 row_rms,
213 ..Default::default()
214 };
215 let (mut err_sq, mut ref_sq) = (0.0_f64, 0.0_f64);
216 for (i, (a, b)) in actual
217 .chunks_exact(2)
218 .zip(reference.chunks_exact(2))
219 .enumerate()
220 {
221 let (ab, bb) = (bits(a), bits(b));
222 let (av, bv) = (val(ab), val(bb));
223 err_sq += (av - bv) * (av - bv);
224 ref_sq += bv * bv;
225 d.max_abs = d.max_abs.max((av - bv).abs());
226 let ulp = (bf16_ord(ab) - bf16_ord(bb)).abs();
227 if av.signum() != bv.signum() && bv.abs() < M16_TC_SIGN_FLIP_BAND {
228 d.sign_flips += 1;
229 continue;
230 }
231 d.max_ulp = d.max_ulp.max(ulp);
232 if ulp > M16_TC_MAX_ULP {
233 d.over_ulp_only += 1;
234 }
235 let row = if n == 0 { 0 } else { i / n };
236 let scale = d.row_rms.get(row).copied().unwrap_or(d.rms);
237 if !within_m16_tc_budget(ab, bb, k, scale) {
238 d.over_budget.push(M16TcOutlier {
239 row,
240 col: if n == 0 { 0 } else { i % n },
241 reference: bv as f32,
242 actual: av as f32,
243 ulp,
244 row_rms: scale,
245 });
246 }
247 }
248 d.rel_rms = if ref_sq > 0.0 {
249 (err_sq / ref_sq).sqrt()
250 } else {
251 err_sq.sqrt()
252 };
253 d
254}