spark_model/layers/deepseek_v41_ref/
engram.rs1use super::{Golden, fixed_value, to_bf16_rne};
24
25pub struct EngramTables {
27 pub layer_ids: Vec<usize>,
28 pub max_ngram: usize,
29 pub n_heads: usize,
30 pub head_dim: usize,
31 pub num_embeddings: Vec<usize>,
32 pub pad_id: i64,
34 pub token_map: Vec<i64>,
35 pub multipliers: Vec<Vec<i64>>,
37 pub primes: Vec<Vec<Vec<i64>>>,
39 pub offsets: Vec<Vec<i64>>,
41 pub scale_block: usize,
43 pub scales: Vec<Vec<f32>>,
45}
46
47impl EngramTables {
48 pub fn n_hash_cols(&self) -> usize {
49 (self.max_ngram - 1) * self.n_heads
50 }
51
52 pub fn from_golden(g: &Golden) -> Self {
53 let e = &g.0["fixture"]["engram"];
54 let ints = |v: &serde_json::Value| -> Vec<i64> {
55 v.as_array()
56 .expect("array")
57 .iter()
58 .map(|x| x.as_i64().expect("int"))
59 .collect()
60 };
61 EngramTables {
62 layer_ids: ints(&e["layer_ids"])
63 .into_iter()
64 .map(|x| x as usize)
65 .collect(),
66 max_ngram: e["max_ngram_size"].as_u64().unwrap() as usize,
67 n_heads: e["n_heads"].as_u64().unwrap() as usize,
68 head_dim: e["head_dim"].as_u64().unwrap() as usize,
69 num_embeddings: ints(&e["num_embeddings"])
70 .into_iter()
71 .map(|x| x as usize)
72 .collect(),
73 pad_id: e["pad_id_compressed"].as_i64().unwrap(),
74 token_map: ints(&e["token_map"]),
75 multipliers: e["multipliers"]
76 .as_array()
77 .unwrap()
78 .iter()
79 .map(ints)
80 .collect(),
81 primes: e["primes"]
82 .as_array()
83 .unwrap()
84 .iter()
85 .map(|l| l.as_array().unwrap().iter().map(ints).collect())
86 .collect(),
87 offsets: e["offsets"].as_array().unwrap().iter().map(ints).collect(),
88 scale_block: e["scale_block"].as_u64().unwrap() as usize,
89 scales: e["scales"]
90 .as_array()
91 .unwrap()
92 .iter()
93 .map(|l| {
94 l.as_array()
95 .unwrap()
96 .iter()
97 .map(|x| x.as_f64().unwrap() as f32)
98 .collect()
99 })
100 .collect(),
101 }
102 }
103}
104
105pub struct NgramHashState<'a> {
108 pub t: &'a EngramTables,
109 pub batch: usize,
110 pub max_seq: usize,
111 cache: Vec<i64>,
112}
113
114impl<'a> NgramHashState<'a> {
115 pub fn new(t: &'a EngramTables, batch: usize, max_seq: usize) -> Self {
116 NgramHashState {
117 t,
118 batch,
119 max_seq,
120 cache: vec![0; batch * max_seq],
121 }
122 }
123
124 pub fn forward(&mut self, input_ids: &[i64], seqlen: usize, start_pos: usize) -> Vec<i64> {
126 let t = self.t;
127 let (b_n, ng, nl, cols) = (self.batch, t.max_ngram, t.layer_ids.len(), t.n_hash_cols());
128 assert_eq!(input_ids.len(), b_n * seqlen);
129 assert!(start_pos + seqlen <= self.max_seq);
130 for b in 0..b_n {
131 for s in 0..seqlen {
132 self.cache[b * self.max_seq + start_pos + s] =
133 t.token_map[input_ids[b * seqlen + s] as usize];
134 }
135 }
136 let mut out = vec![0i64; b_n * seqlen * nl * cols];
137 let mut tokens = vec![0i64; ng];
138 for b in 0..b_n {
139 for s in 0..seqlen {
140 let pos = start_pos + s;
141 let mut blocked = false;
143 for (shift, tok) in tokens.iter_mut().enumerate() {
144 let src = self.cache[b * self.max_seq + pos.saturating_sub(shift)];
145 blocked = blocked || pos < shift || src == -1;
146 *tok = if blocked { t.pad_id } else { src };
147 }
148 for (li, mults) in t.multipliers.iter().enumerate() {
149 let mut rolling = tokens[0].wrapping_mul(mults[0]);
150 for i in 1..ng {
151 rolling ^= tokens[i].wrapping_mul(mults[i]);
152 for h in 0..t.n_heads {
153 let col = (i - 1) * t.n_heads + h;
154 out[((b * seqlen + s) * nl + li) * cols + col] =
155 rolling.rem_euclid(t.primes[li][i - 1][h]) + t.offsets[li][col];
156 }
157 }
158 }
159 }
160 }
161 out
162 }
163}
164
165pub fn f32_to_e4m3_rne(x: f32) -> u8 {
170 if x.is_nan() {
171 return 0x7F;
172 }
173 let sign: u8 = if x.is_sign_negative() { 0x80 } else { 0 };
174 let bits = x.abs().to_bits();
175 let exp_field = ((bits >> 23) & 0xFF) as i32;
176 if exp_field == 0 {
177 return sign; }
179 let a = x.abs() as f64;
180 if a > 448.0 {
181 return sign | 0x7E;
182 }
183 let e = exp_field - 127;
184 if e < -6 {
185 let r = (a * 512.0).round_ties_even() as u8;
187 return sign | r; }
189 let mant = ((a * 2f64.powi(-e) - 1.0) * 8.0).round_ties_even() as u8;
190 let (e, mant) = if mant == 8 { (e + 1, 0u8) } else { (e, mant) };
191 sign | (((e + 7) as u8) << 3) | mant
192}
193
194pub fn e4m3_to_f32(b: u8) -> f32 {
195 let sign = if b & 0x80 != 0 { -1.0f32 } else { 1.0 };
196 let exp_field = ((b >> 3) & 0x0F) as i32;
197 let mant = (b & 0x07) as f32;
198 if exp_field == 15 && mant == 7.0 {
199 return f32::NAN;
200 }
201 if exp_field == 0 {
202 return sign * mant / 8.0 * 2f32.powi(-6);
203 }
204 sign * (1.0 + mant / 8.0) * 2f32.powi(exp_field - 7)
205}
206
207pub fn f32_to_e8m0_rne(x: f32) -> f32 {
211 let bits = x.to_bits();
212 let mut e = ((bits >> 23) & 0xFF) as i32;
213 let m = bits & 0x7F_FFFF;
214 if m > 0x40_0000 || (m == 0x40_0000 && (e & 1) == 1) {
215 e += 1;
216 }
217 f32::from_bits((e as u32) << 23)
218}
219
220pub fn regen_table(t: &EngramTables, layer_idx: usize) -> Vec<f32> {
224 let lid = t.layer_ids[layer_idx];
225 let (rows, hd, blk) = (t.num_embeddings[layer_idx], t.head_dim, t.scale_block);
226 let name = format!("layers.{lid}.engram.embed.weight");
227 let scales = &t.scales[layer_idx];
228 (0..(rows * hd) as u64)
229 .map(|i| {
230 let v = e4m3_to_f32(f32_to_e4m3_rne(fixed_value(&name, i, 0.5, 0.0)));
231 let (r, d) = ((i as usize) / hd, (i as usize) % hd);
232 to_bf16_rne(v * scales[r * (hd / blk) + d / blk])
233 })
234 .collect()
235}
236
237pub fn embed_rows(table: &[f32], head_dim: usize, hash_ids: &[i64]) -> Vec<f32> {
240 let mut out = Vec::with_capacity(hash_ids.len() * head_dim);
241 for &id in hash_ids {
242 let r = id as usize * head_dim;
243 out.extend_from_slice(&table[r..r + head_dim]);
244 }
245 out
246}
247
248pub fn regen_bf16_matrix(name: &str, out_f: usize, in_f: usize) -> Vec<f32> {
250 let scale = (in_f as f64).powf(-0.5);
251 (0..(out_f * in_f) as u64)
252 .map(|i| to_bf16_rne(fixed_value(name, i, scale, 0.0)))
253 .collect()
254}
255
256pub fn regen_bf16_qk(name: &str, n: usize) -> Vec<f32> {
258 (0..n as u64)
259 .map(|i| to_bf16_rne(fixed_value(name, i, 0.05, 1.0)))
260 .collect()
261}
262
263pub fn linear_bf16(x: &[f32], rows: usize, in_f: usize, w: &[f32], out_f: usize) -> Vec<f32> {
265 let mut y = vec![0f32; rows * out_f];
266 for r in 0..rows {
267 let xr = &x[r * in_f..(r + 1) * in_f];
268 for o in 0..out_f {
269 let wr = &w[o * in_f..(o + 1) * in_f];
270 let acc: f32 = xr.iter().zip(wr).map(|(a, b)| a * b).sum();
271 y[r * out_f + o] = to_bf16_rne(acc);
272 }
273 }
274 y
275}
276
277pub fn gate_and_add(
280 x: &[f32],
281 kv: &[f32],
282 q_w: &[f32],
283 k_w: &[f32],
284 tokens: usize,
285 hc: usize,
286 dim: usize,
287 eps: f32,
288) -> Vec<f32> {
289 let kv_w = dim * (hc + 1);
290 let inv_sqrt_dim = (dim as f32).powf(-0.5);
291 let mut out = vec![0f32; tokens * hc * dim];
292 for t in 0..tokens {
293 let value = &kv[t * kv_w + hc * dim..(t + 1) * kv_w];
294 for c in 0..hc {
295 let h = &x[(t * hc + c) * dim..(t * hc + c + 1) * dim];
296 let key = &kv[t * kv_w + c * dim..t * kv_w + (c + 1) * dim];
297 let (mut hh, mut kk, mut dot) = (0f32, 0f32, 0f32);
298 for d in 0..dim {
299 hh += h[d] * h[d];
300 kk += key[d] * key[d];
301 dot += h[d] * (q_w[c * dim + d] * k_w[c * dim + d]) * key[d];
303 }
304 let rstd =
305 (hh / dim as f32 + eps).sqrt().recip() * (kk / dim as f32 + eps).sqrt().recip();
306 let dot = dot * rstd * inv_sqrt_dim;
307 let g = dot.abs().max(1e-6).sqrt().copysign(dot);
309 let gate = 1.0 / (1.0 + (-g).exp());
310 for d in 0..dim {
311 out[(t * hc + c) * dim + d] = to_bf16_rne(h[d] + gate * value[d]);
312 }
313 }
314 }
315 out
316}
317
318#[cfg(test)]
319#[path = "engram_tests.rs"]
320mod tests;