metrale_model_arch/deepseek_v41_ref/
engram.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// provenance-id: 526f6e616c6420522e205374657369616b
3
4//! 2026-09-25: DeepSeek-V4.1 engram, CPU reference, checked stage by stage
5//! against the golden:
6//!
7//! 1. Hash (`NgramHashState`): compressed token map, n-gram window with pad
8//!    and a cache that carries look-back across the prefill/decode split,
9//!    per-lookback multiply, XOR-rolled, then `% prime` per (n-gram size,
10//!    head) plus that slot's offset. Integer-exact.
11//! 2. Table rows (`regen_table`, `embed_rows`): the fp8 e4m3 table,
12//!    dequantised per `scale_block` values by a power-of-two scale, gathered
13//!    by hash id. An e4m3 value times a power of two is exact in bf16, so
14//!    this stage is exact too.
15//! 3. Projection (`wkv`): one bf16 GEMM, `[n_hash_cols * head_dim] -> [dim *
16//!    (hc_mult + 1)]`, split into one key per hyper-connection copy plus one
17//!    shared value.
18//! 4. Gate (`gate_and_add`): normalised dot product of stream against key,
19//!    signed-sqrt sigmoid, `h + gate * value`, back to bf16.
20//!
21//! The hash tables (`token_map`, `multipliers`, `primes`, `offsets`) are
22//! read from the golden, never recomputed, as the loader reads them from the
23//! GGUF's `deepseek41.engram.*` metadata.
24//!
25//! Owner: model-arch, DeepSeek-V4.1 reference.
26//! Invariants: none beyond the types.
27
28use super::{Golden, fixed_value, to_bf16_rne};
29
30/// 2026-09-25: The engram hash tables, geometry and table scales, from the
31/// golden's `fixture.engram`.
32pub struct EngramTables {
33    pub layer_ids: Vec<usize>,
34    pub max_ngram: usize,
35    pub n_heads: usize,
36    pub head_dim: usize,
37    pub num_embeddings: Vec<usize>,
38    // 2026-09-25: `pad_id` is the pad in the compressed id space. Layouts:
39    // `multipliers[layer][lookback]`, `primes[layer][ngram_size - 2][head]`,
40    // `offsets[layer][(ngram_size - 2) * n_heads + head]`, and
41    // `scales[layer][row * (head_dim / scale_block) + block]`, powers of two
42    // with `scale_block` values each along `head_dim`.
43    pub pad_id: i64,
44    pub token_map: Vec<i64>,
45    pub multipliers: Vec<Vec<i64>>,
46    pub primes: Vec<Vec<Vec<i64>>>,
47    pub offsets: Vec<Vec<i64>>,
48    pub scale_block: usize,
49    pub scales: Vec<Vec<f32>>,
50}
51
52impl EngramTables {
53    pub fn n_hash_cols(&self) -> usize {
54        (self.max_ngram - 1) * self.n_heads
55    }
56
57    pub fn from_golden(g: &Golden) -> Self {
58        let e = &g.0["fixture"]["engram"];
59        let ints = |v: &serde_json::Value| -> Vec<i64> {
60            v.as_array()
61                .expect("array")
62                .iter()
63                .map(|x| x.as_i64().expect("int"))
64                .collect()
65        };
66        EngramTables {
67            layer_ids: ints(&e["layer_ids"])
68                .into_iter()
69                .map(|x| x as usize)
70                .collect(),
71            max_ngram: e["max_ngram_size"].as_u64().unwrap() as usize,
72            n_heads: e["n_heads"].as_u64().unwrap() as usize,
73            head_dim: e["head_dim"].as_u64().unwrap() as usize,
74            num_embeddings: ints(&e["num_embeddings"])
75                .into_iter()
76                .map(|x| x as usize)
77                .collect(),
78            pad_id: e["pad_id_compressed"].as_i64().unwrap(),
79            token_map: ints(&e["token_map"]),
80            multipliers: e["multipliers"]
81                .as_array()
82                .unwrap()
83                .iter()
84                .map(ints)
85                .collect(),
86            primes: e["primes"]
87                .as_array()
88                .unwrap()
89                .iter()
90                .map(|l| l.as_array().unwrap().iter().map(ints).collect())
91                .collect(),
92            offsets: e["offsets"].as_array().unwrap().iter().map(ints).collect(),
93            scale_block: e["scale_block"].as_u64().unwrap() as usize,
94            scales: e["scales"]
95                .as_array()
96                .unwrap()
97                .iter()
98                .map(|l| {
99                    l.as_array()
100                        .unwrap()
101                        .iter()
102                        .map(|x| x.as_f64().unwrap() as f32)
103                        .collect()
104                })
105                .collect(),
106        }
107    }
108}
109
110/// 2026-09-25: The n-gram hash state (the reference's `NgramHashState`). The cache
111/// holds compressed ids for every position seen, so a decode step's look-back
112/// reaches into the prefill.
113pub struct NgramHashState<'a> {
114    pub t: &'a EngramTables,
115    pub batch: usize,
116    pub max_seq: usize,
117    cache: Vec<i64>,
118}
119
120impl<'a> NgramHashState<'a> {
121    pub fn new(t: &'a EngramTables, batch: usize, max_seq: usize) -> Self {
122        NgramHashState {
123            t,
124            batch,
125            max_seq,
126            cache: vec![0; batch * max_seq],
127        }
128    }
129
130    /// 2026-09-25: `input_ids`: `[batch, seqlen]` row-major. Returns `[batch,
131    /// seqlen, n_layers, n_hash_cols]`.
132    pub fn forward(&mut self, input_ids: &[i64], seqlen: usize, start_pos: usize) -> Vec<i64> {
133        let t = self.t;
134        let (b_n, ng, nl, cols) = (self.batch, t.max_ngram, t.layer_ids.len(), t.n_hash_cols());
135        assert_eq!(input_ids.len(), b_n * seqlen);
136        assert!(start_pos + seqlen <= self.max_seq);
137        for b in 0..b_n {
138            for s in 0..seqlen {
139                self.cache[b * self.max_seq + start_pos + s] =
140                    t.token_map[input_ids[b * seqlen + s] as usize];
141            }
142        }
143        let mut out = vec![0i64; b_n * seqlen * nl * cols];
144        let mut tokens = vec![0i64; ng];
145        for b in 0..b_n {
146            for s in 0..seqlen {
147                let pos = start_pos + s;
148                // 2026-09-25: `blocked` stays set for every larger shift once a
149                // shift reaches before position 0 or a -1 compressed id.
150                let mut blocked = false;
151                for (shift, tok) in tokens.iter_mut().enumerate() {
152                    let src = self.cache[b * self.max_seq + pos.saturating_sub(shift)];
153                    blocked = blocked || pos < shift || src == -1;
154                    *tok = if blocked { t.pad_id } else { src };
155                }
156                for (li, mults) in t.multipliers.iter().enumerate() {
157                    let mut rolling = tokens[0].wrapping_mul(mults[0]);
158                    for i in 1..ng {
159                        rolling ^= tokens[i].wrapping_mul(mults[i]);
160                        for h in 0..t.n_heads {
161                            let col = (i - 1) * t.n_heads + h;
162                            out[((b * seqlen + s) * nl + li) * cols + col] =
163                                rolling.rem_euclid(t.primes[li][i - 1][h]) + t.offsets[li][col];
164                        }
165                    }
166                }
167            }
168        }
169        out
170    }
171}
172
173/// 2026-09-25: Encode to fp8 e4m3fn with round-to-nearest-even. NaN -> 0x7F;
174/// above 448 saturates to 448 (0x7E).
175pub fn f32_to_e4m3_rne(x: f32) -> u8 {
176    if x.is_nan() {
177        return 0x7F;
178    }
179    let sign: u8 = if x.is_sign_negative() { 0x80 } else { 0 };
180    let bits = x.abs().to_bits();
181    let exp_field = ((bits >> 23) & 0xFF) as i32;
182    if exp_field == 0 {
183        return sign;
184    }
185    let a = x.abs() as f64;
186    if a > 448.0 {
187        return sign | 0x7E;
188    }
189    let e = exp_field - 127;
190    if e < -6 {
191        // 2026-09-25: e4m3 subnormal: value = m/8 * 2^-6, m in 0..=8; m == 8 is
192        // the first normal, whose code (exp_field 1, mantissa 0) is 0x08.
193        let r = (a * 512.0).round_ties_even() as u8;
194        return sign | r;
195    }
196    let mant = ((a * 2f64.powi(-e) - 1.0) * 8.0).round_ties_even() as u8;
197    let (e, mant) = if mant == 8 { (e + 1, 0u8) } else { (e, mant) };
198    sign | (((e + 7) as u8) << 3) | mant
199}
200
201pub fn e4m3_to_f32(b: u8) -> f32 {
202    let sign = if b & 0x80 != 0 { -1.0f32 } else { 1.0 };
203    let exp_field = ((b >> 3) & 0x0F) as i32;
204    let mant = (b & 0x07) as f32;
205    if exp_field == 15 && mant == 7.0 {
206        return f32::NAN;
207    }
208    if exp_field == 0 {
209        return sign * mant / 8.0 * 2f32.powi(-6);
210    }
211    sign * (1.0 + mant / 8.0) * 2f32.powi(exp_field - 7)
212}
213
214/// 2026-09-25: f32 -> e8m0 as a power of two: nearest exponent, ties (exactly
215/// 1.5 * 2^e) to the even exponent. Pinned against the golden's scales by
216/// `e8m0_rounding_rule_matches_torch`.
217pub fn f32_to_e8m0_rne(x: f32) -> f32 {
218    let bits = x.to_bits();
219    let mut e = ((bits >> 23) & 0xFF) as i32;
220    let m = bits & 0x7F_FFFF;
221    if m > 0x40_0000 || (m == 0x40_0000 && (e & 1) == 1) {
222        e += 1;
223    }
224    f32::from_bits((e as u32) << 23)
225}
226
227/// 2026-09-25: The fixture's fp8 table for layer index `layer_idx`, regenerated
228/// and dequantised with the golden's per-block scales (value times scale,
229/// then bf16). `[rows * head_dim]` f32; exact, since e4m3 times a power of
230/// two is bf16-exact.
231pub fn regen_table(t: &EngramTables, layer_idx: usize) -> Vec<f32> {
232    let lid = t.layer_ids[layer_idx];
233    let (rows, hd, blk) = (t.num_embeddings[layer_idx], t.head_dim, t.scale_block);
234    let name = format!("layers.{lid}.engram.embed.weight");
235    let scales = &t.scales[layer_idx];
236    (0..(rows * hd) as u64)
237        .map(|i| {
238            let v = e4m3_to_f32(f32_to_e4m3_rne(fixed_value(&name, i, 0.5, 0.0)));
239            let (r, d) = ((i as usize) / hd, (i as usize) % hd);
240            to_bf16_rne(v * scales[r * (hd / blk) + d / blk])
241        })
242        .collect()
243}
244
245/// 2026-09-25: Gather rows of a dequantised table by hash id. Output `[n_ids, head_dim]`.
246pub fn embed_rows(table: &[f32], head_dim: usize, hash_ids: &[i64]) -> Vec<f32> {
247    let mut out = Vec::with_capacity(hash_ids.len() * head_dim);
248    for &id in hash_ids {
249        let r = id as usize * head_dim;
250        out.extend_from_slice(&table[r..r + head_dim]);
251    }
252    out
253}
254
255/// 2026-09-25: A bf16-stored `[out, in]` weight regenerated by the fixture rule
256/// `scale = in^-0.5`, offset 0.
257pub fn regen_bf16_matrix(name: &str, out_f: usize, in_f: usize) -> Vec<f32> {
258    let scale = (in_f as f64).powf(-0.5);
259    (0..(out_f * in_f) as u64)
260        .map(|i| to_bf16_rne(fixed_value(name, i, scale, 0.0)))
261        .collect()
262}
263
264/// 2026-09-25: A bf16-stored engram q/k weight of `n` values regenerated by the
265/// fixture rule (offset 1, scale 0.05).
266pub fn regen_bf16_qk(name: &str, n: usize) -> Vec<f32> {
267    (0..n as u64)
268        .map(|i| to_bf16_rne(fixed_value(name, i, 0.05, 1.0)))
269        .collect()
270}
271
272/// 2026-09-25: bf16 linear: f32 accumulate, bf16 output. `x`: `[rows, in]`, `w`:
273/// `[out, in]`.
274pub fn linear_bf16(x: &[f32], rows: usize, in_f: usize, w: &[f32], out_f: usize) -> Vec<f32> {
275    let mut y = vec![0f32; rows * out_f];
276    for r in 0..rows {
277        let xr = &x[r * in_f..(r + 1) * in_f];
278        for o in 0..out_f {
279            let wr = &w[o * in_f..(o + 1) * in_f];
280            let acc: f32 = xr.iter().zip(wr).map(|(a, b)| a * b).sum();
281            y[r * out_f + o] = to_bf16_rne(acc);
282        }
283    }
284    y
285}
286
287/// 2026-09-25: The engram after the projection: `x` `[tokens, hc, dim]` (bf16
288/// values), `kv` `[tokens, dim*(hc+1)]`, q/k weights `[hc, dim]`. Returns
289/// bf16-rounded `[tokens, hc, dim]`.
290pub fn gate_and_add(
291    x: &[f32],
292    kv: &[f32],
293    q_w: &[f32],
294    k_w: &[f32],
295    tokens: usize,
296    hc: usize,
297    dim: usize,
298    eps: f32,
299) -> Vec<f32> {
300    let kv_w = dim * (hc + 1);
301    let inv_sqrt_dim = (dim as f32).powf(-0.5);
302    let mut out = vec![0f32; tokens * hc * dim];
303    for t in 0..tokens {
304        let value = &kv[t * kv_w + hc * dim..(t + 1) * kv_w];
305        for c in 0..hc {
306            let h = &x[(t * hc + c) * dim..(t * hc + c + 1) * dim];
307            let key = &kv[t * kv_w + c * dim..t * kv_w + (c + 1) * dim];
308            let (mut hh, mut kk, mut dot) = (0f32, 0f32, 0f32);
309            for d in 0..dim {
310                hh += h[d] * h[d];
311                kk += key[d] * key[d];
312                // 2026-09-25: The q and k weights enter only as their product.
313                dot += h[d] * (q_w[c * dim + d] * k_w[c * dim + d]) * key[d];
314            }
315            let rstd =
316                (hh / dim as f32 + eps).sqrt().recip() * (kk / dim as f32 + eps).sqrt().recip();
317            let dot = dot * rstd * inv_sqrt_dim;
318            // 2026-09-25: Signed sqrt (magnitude floored at 1e-6) before the sigmoid.
319            let g = dot.abs().max(1e-6).sqrt().copysign(dot);
320            let gate = 1.0 / (1.0 + (-g).exp());
321            for d in 0..dim {
322                out[(t * hc + c) * dim + d] = to_bf16_rne(h[d] + gate * value[d]);
323            }
324        }
325    }
326    out
327}
328
329#[cfg(test)]
330#[path = "engram_tests.rs"]
331mod tests;