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}