metrale_model_arch/glm5next_kda_ref/
mod.rs1#[derive(Clone, Copy, Debug)]
36pub struct KdaDims {
37 pub hidden: usize,
38 pub heads: usize,
39 pub head_dim: usize,
40 pub tokens: usize,
41}
42
43#[inline]
44fn sigmoid(x: f32) -> f32 {
45 1.0 / (1.0 + (-x).exp())
46}
47
48pub fn l2norm_rows(x: &[f32], d: usize, eps: f32) -> Vec<f32> {
51 let mut out = vec![0.0f32; x.len()];
52 for (row_in, row_out) in x.chunks_exact(d).zip(out.chunks_exact_mut(d)) {
53 let inv = 1.0 / (row_in.iter().map(|v| v * v).sum::<f32>() + eps).sqrt();
54 for (o, i) in row_out.iter_mut().zip(row_in) {
55 *o = i * inv;
56 }
57 }
58 out
59}
60
61pub fn linear(x: &[f32], m: usize, k: usize, w: &[f32], n: usize) -> Vec<f32> {
63 let mut out = vec![0.0f32; m * n];
64 for row in 0..m {
65 for col in 0..n {
66 let mut acc = 0.0f32;
67 for i in 0..k {
68 acc += x[row * k + i] * w[col * k + i];
69 }
70 out[row * n + col] = acc;
71 }
72 }
73 out
74}
75
76pub fn bounded_gate(
82 g_lowrank: &[f32],
83 dt_bias: &[f32],
84 a_log: &[f32],
85 dims: KdaDims,
86 lower_bound: f32,
87) -> Vec<f32> {
88 let (h_n, d) = (dims.heads, dims.head_dim);
89 let mut out = vec![0.0f32; dims.tokens * h_n * d];
90 for t in 0..dims.tokens {
91 for h in 0..h_n {
92 let decay = a_log[h].exp();
93 for dd in 0..d {
94 let ch = h * d + dd;
95 let g = g_lowrank[t * h_n * d + ch] + dt_bias[ch];
96 out[(t * h_n + h) * d + dd] = lower_bound * sigmoid(decay * g);
97 }
98 }
99 }
100 out
101}
102
103pub fn unbounded_gdn_gate(g_raw: f32, dt_bias: f32, a_log: f32) -> f32 {
108 let x = g_raw + dt_bias;
109 let softplus = if x > 20.0 { x } else { (1.0 + x.exp()).ln() };
111 -a_log.exp() * softplus
112}
113
114pub fn kda_recurrent(
126 q: &[f32],
127 k: &[f32],
128 v: &[f32],
129 gate: &[f32],
130 beta: &[f32],
131 dims: KdaDims,
132 state: &mut [f32],
133) -> Vec<f32> {
134 let d = dims.head_dim;
135 let qn = l2norm_rows(q, d, 1e-6);
136 let kn = l2norm_rows(k, d, 1e-6);
137 kda_recurrent_prenorm(&qn, &kn, v, gate, beta, dims, state)
138}
139
140pub fn kda_recurrent_prenorm(
147 qn: &[f32],
148 kn: &[f32],
149 v: &[f32],
150 gate: &[f32],
151 beta: &[f32],
152 dims: KdaDims,
153 state: &mut [f32],
154) -> Vec<f32> {
155 let (h_n, d, t_n) = (dims.heads, dims.head_dim, dims.tokens);
156 let scale = 1.0 / (d as f32).sqrt();
157
158 let mut out = vec![0.0f32; t_n * h_n * d];
159 let mut delta = vec![0.0f32; d];
160 for t in 0..t_n {
161 for h in 0..h_n {
162 let base = (t * h_n + h) * d;
163 let s = &mut state[h * d * d..(h + 1) * d * d];
164
165 for kd in 0..d {
166 let decay = gate[base + kd].exp();
167 for vd in 0..d {
168 s[kd * d + vd] *= decay;
169 }
170 }
171 let b = beta[t * h_n + h];
172 for vd in 0..d {
173 let mut kv = 0.0f32;
174 for kd in 0..d {
175 kv += s[kd * d + vd] * kn[base + kd];
176 }
177 delta[vd] = (v[base + vd] - kv) * b;
178 }
179 for kd in 0..d {
180 let kk = kn[base + kd];
181 for vd in 0..d {
182 s[kd * d + vd] += kk * delta[vd];
183 }
184 }
185 for vd in 0..d {
186 let mut acc = 0.0f32;
187 for kd in 0..d {
188 acc += s[kd * d + vd] * qn[base + kd] * scale;
189 }
190 out[base + vd] = acc;
191 }
192 }
193 }
194 out
195}
196
197pub fn kda_chunked(
201 q: &[f32],
202 k: &[f32],
203 v: &[f32],
204 gate: &[f32],
205 beta: &[f32],
206 dims: KdaDims,
207 chunk: usize,
208 state: &mut [f32],
209) -> Vec<f32> {
210 let d = dims.head_dim;
211 let qn = l2norm_rows(q, d, 1e-6);
213 let kn = l2norm_rows(k, d, 1e-6);
214 kda_chunked_prenorm(&qn, &kn, v, gate, beta, dims, chunk, state)
215}
216
217#[allow(clippy::too_many_arguments)]
221pub fn kda_chunked_prenorm(
222 qn: &[f32],
223 kn: &[f32],
224 v: &[f32],
225 gate: &[f32],
226 beta: &[f32],
227 dims: KdaDims,
228 chunk: usize,
229 state: &mut [f32],
230) -> Vec<f32> {
231 let (h_n, d, t_n) = (dims.heads, dims.head_dim, dims.tokens);
232 let scale = 1.0 / (d as f32).sqrt();
233 let pad = (chunk - t_n % chunk) % chunk;
234 let tt = t_n + pad;
235 let n_chunks = tt / chunk;
236
237 let mut out = vec![0.0f32; t_n * h_n * d];
238 for h in 0..h_n {
239 let mut qh = vec![0.0f32; tt * d];
242 let mut kh = vec![0.0f32; tt * d];
243 let mut vh = vec![0.0f32; tt * d];
244 let mut gh = vec![0.0f32; tt * d];
245 let mut bh = vec![0.0f32; tt];
246 for t in 0..t_n {
247 let src = (t * h_n + h) * d;
248 for dd in 0..d {
249 qh[t * d + dd] = qn[src + dd] * scale;
250 kh[t * d + dd] = kn[src + dd];
251 vh[t * d + dd] = v[src + dd];
252 gh[t * d + dd] = gate[src + dd];
253 }
254 bh[t] = beta[t * h_n + h];
255 }
256
257 let s = &mut state[h * d * d..(h + 1) * d * d];
258 for c in 0..n_chunks {
259 let off = c * chunk;
260 let mut gc = vec![0.0f32; chunk * d];
262 for i in 0..chunk {
263 for dd in 0..d {
264 let prev = if i == 0 { 0.0 } else { gc[(i - 1) * d + dd] };
265 gc[i * d + dd] = prev + gh[(off + i) * d + dd];
266 }
267 }
268 let dmask = |i: usize, j: usize, dd: usize| (gc[i * d + dd] - gc[j * d + dd]).exp();
269
270 let mut attn = vec![0.0f32; chunk * chunk];
273 for i in 0..chunk {
274 for j in 0..i {
275 let mut acc = 0.0f32;
276 for dd in 0..d {
277 acc += kh[(off + i) * d + dd]
278 * bh[off + i]
279 * kh[(off + j) * d + dd]
280 * dmask(i, j, dd);
281 }
282 attn[i * chunk + j] = -acc;
283 }
284 }
285 for i in 1..chunk {
287 let row: Vec<f32> = (0..i).map(|j| attn[i * chunk + j]).collect();
288 for j in 0..i {
289 let mut acc = 0.0f32;
290 for (m, r) in row.iter().enumerate() {
291 acc += r * attn[m * chunk + j];
292 }
293 attn[i * chunk + j] = row[j] + acc;
294 }
295 }
296 for i in 0..chunk {
297 attn[i * chunk + i] = 1.0;
298 }
299
300 let mut value = vec![0.0f32; chunk * d];
302 let mut k_cumdecay = vec![0.0f32; chunk * d];
303 for i in 0..chunk {
304 for dd in 0..d {
305 let (mut av, mut ak) = (0.0f32, 0.0f32);
306 for j in 0..chunk {
307 let a = attn[i * chunk + j];
308 av += a * vh[(off + j) * d + dd] * bh[off + j];
309 ak += a * kh[(off + j) * d + dd] * bh[off + j] * gc[j * d + dd].exp();
310 }
311 value[i * d + dd] = av;
312 k_cumdecay[i * d + dd] = ak;
313 }
314 }
315
316 let mut v_new = vec![0.0f32; chunk * d];
318 for i in 0..chunk {
319 for vd in 0..d {
320 let mut vp = 0.0f32;
321 for kd in 0..d {
322 vp += k_cumdecay[i * d + kd] * s[kd * d + vd];
323 }
324 v_new[i * d + vd] = value[i * d + vd] - vp;
325 }
326 }
327
328 for i in 0..chunk {
330 if off + i >= t_n {
331 continue;
332 }
333 let dst = ((off + i) * h_n + h) * d;
334 for vd in 0..d {
335 let mut acc = 0.0f32;
336 for kd in 0..d {
337 acc += qh[(off + i) * d + kd] * gc[i * d + kd].exp() * s[kd * d + vd];
338 }
339 out[dst + vd] = acc;
340 }
341 for j in 0..=i {
342 let mut intra = 0.0f32;
343 for dd in 0..d {
344 intra += qh[(off + i) * d + dd] * kh[(off + j) * d + dd] * dmask(i, j, dd);
345 }
346 for vd in 0..d {
347 out[dst + vd] += intra * v_new[j * d + vd];
348 }
349 }
350 }
351
352 let last = (chunk - 1) * d;
354 for kd in 0..d {
355 let gl = gc[last + kd];
356 for vd in 0..d {
357 s[kd * d + vd] *= gl.exp();
358 }
359 for i in 0..chunk {
360 let w = kh[(off + i) * d + kd] * (gl - gc[i * d + kd]).exp();
361 for vd in 0..d {
362 s[kd * d + vd] += w * v_new[i * d + vd];
363 }
364 }
365 }
366 }
367 }
368 out
369}
370
371pub fn rms_norm_gated(x: &[f32], weight: &[f32], gate: &[f32], d: usize, eps: f32) -> Vec<f32> {
374 let mut out = vec![0.0f32; x.len()];
375 for i in (0..x.len()).step_by(d) {
376 let var = x[i..i + d].iter().map(|v| v * v).sum::<f32>() / d as f32;
377 let inv = 1.0 / (var + eps).sqrt();
378 for dd in 0..d {
379 out[i + dd] = x[i + dd] * inv * weight[dd] * sigmoid(gate[i + dd]);
380 }
381 }
382 out
383}
384
385pub struct KdaWeights<'a> {
389 pub w_f_a: &'a [f32],
390 pub w_f_b: &'a [f32],
391 pub dt_bias: &'a [f32],
392 pub a_log: &'a [f32],
393 pub w_b: &'a [f32],
394 pub w_g_a: &'a [f32],
395 pub w_g_b: &'a [f32],
396 pub o_norm_w: &'a [f32],
397 pub w_o: &'a [f32],
398}
399
400#[allow(clippy::too_many_arguments)]
403pub fn kda_reference_layer(
404 hidden: &[f32],
405 q: &[f32],
406 k: &[f32],
407 v: &[f32],
408 w: &KdaWeights<'_>,
409 dims: KdaDims,
410 lower_bound: f32,
411 rms_eps: f32,
412 state: &mut [f32],
413) -> Vec<f32> {
414 let (t_n, h_n, d, hid) = (dims.tokens, dims.heads, dims.head_dim, dims.hidden);
415
416 let f_a = linear(hidden, t_n, hid, w.w_f_a, d);
417 let g_lowrank = linear(&f_a, t_n, d, w.w_f_b, h_n * d);
418 let gate = bounded_gate(&g_lowrank, w.dt_bias, w.a_log, dims, lower_bound);
419
420 let beta: Vec<f32> = linear(hidden, t_n, hid, w.w_b, h_n)
421 .iter()
422 .map(|x| sigmoid(*x))
423 .collect();
424
425 let core = kda_recurrent(q, k, v, &gate, &beta, dims, state);
426
427 let g_a = linear(hidden, t_n, hid, w.w_g_a, d);
428 let out_gate = linear(&g_a, t_n, d, w.w_g_b, h_n * d);
429 let normed = rms_norm_gated(&core, w.o_norm_w, &out_gate, d, rms_eps);
430
431 linear(&normed, t_n, h_n * d, w.w_o, hid)
432}
433
434#[cfg(test)]
435mod tests;