1use crate::pool::Pool;
22
23pub trait Fp:
28 Copy
29 + PartialOrd
30 + core::ops::Add<Output = Self>
31 + core::ops::Sub<Output = Self>
32 + core::ops::Mul<Output = Self>
33 + core::ops::Div<Output = Self>
34 + core::ops::Neg<Output = Self>
35 + core::ops::AddAssign
36 + core::ops::MulAssign
37 + Send
38 + Sync
39 + 'static
40{
41 const ZERO: Self;
42 const ONE: Self;
43 fn exp(self) -> Self;
44 fn sqrt(self) -> Self;
45 fn maxf(self, o: Self) -> Self;
46 fn fromf(x: f64) -> Self;
47 fn f64(self) -> f64;
48}
49
50impl Fp for f32 {
51 const ZERO: Self = 0.0;
52 const ONE: Self = 1.0;
53 #[inline]
54 fn exp(self) -> Self {
55 f32::exp(self)
56 }
57 #[inline]
58 fn sqrt(self) -> Self {
59 f32::sqrt(self)
60 }
61 #[inline]
62 fn maxf(self, o: Self) -> Self {
63 f32::max(self, o)
64 }
65 #[inline]
66 fn fromf(x: f64) -> Self {
67 x as f32
68 }
69 #[inline]
70 fn f64(self) -> f64 {
71 self as f64
72 }
73}
74
75impl Fp for f64 {
76 const ZERO: Self = 0.0;
77 const ONE: Self = 1.0;
78 #[inline]
79 fn exp(self) -> Self {
80 f64::exp(self)
81 }
82 #[inline]
83 fn sqrt(self) -> Self {
84 f64::sqrt(self)
85 }
86 #[inline]
87 fn maxf(self, o: Self) -> Self {
88 f64::max(self, o)
89 }
90 #[inline]
91 fn fromf(x: f64) -> Self {
92 x
93 }
94 #[inline]
95 fn f64(self) -> f64 {
96 self
97 }
98}
99
100#[inline]
101fn dot<F: Fp>(a: &[F], b: &[F]) -> F {
107 let mut acc = [F::ZERO; 8];
108 let mut ca = a.chunks_exact(8);
109 let mut cb = b.chunks_exact(8);
110 for (x, y) in (&mut ca).zip(&mut cb) {
111 for l in 0..8 {
112 acc[l] += x[l] * y[l];
113 }
114 }
115 let mut s = F::ZERO;
116 for (x, y) in ca.remainder().iter().zip(cb.remainder()) {
117 s += *x * *y;
118 }
119 ((acc[0] + acc[1]) + (acc[2] + acc[3])) + ((acc[4] + acc[5]) + (acc[6] + acc[7])) + s
120}
121
122pub fn matmul_nt<F: Fp>(x: &[F], w: &[F], y: &mut [F], n: usize, k: usize, m: usize) {
127 for i in 0..n {
128 let xr = &x[i * k..(i + 1) * k];
129 for o in 0..m {
130 y[i * m + o] = dot(xr, &w[o * k..(o + 1) * k]);
131 }
132 }
133}
134
135pub fn matmul_nt_dx<F: Fp>(dy: &[F], w: &[F], dx: &mut [F], n: usize, k: usize, m: usize) {
137 for i in 0..n {
138 let dxr = &mut dx[i * k..(i + 1) * k];
139 for o in 0..m {
140 let g = dy[i * m + o];
141 for (d, wv) in dxr.iter_mut().zip(&w[o * k..(o + 1) * k]) {
142 *d += g * *wv;
143 }
144 }
145 }
146}
147
148pub fn matmul_nt_dw<F: Fp>(dy: &[F], x: &[F], dw: &mut [F], n: usize, k: usize, m: usize) {
150 for i in 0..n {
151 let xr = &x[i * k..(i + 1) * k];
152 for o in 0..m {
153 let g = dy[i * m + o];
154 for (d, xv) in dw[o * k..(o + 1) * k].iter_mut().zip(xr) {
155 *d += g * *xv;
156 }
157 }
158 }
159}
160
161const GEMM_BLOCK: usize = 128;
166
167struct SendMut<T>(*mut T);
170unsafe impl<T> Send for SendMut<T> {}
171unsafe impl<T> Sync for SendMut<T> {}
172impl<T> SendMut<T> {
173 #[inline]
174 #[allow(clippy::mut_from_ref)]
178 unsafe fn slice(&self, off: usize, len: usize) -> &mut [T] {
179 unsafe { std::slice::from_raw_parts_mut(self.0.add(off), len) }
180 }
181}
182
183#[cfg(target_os = "macos")]
189mod accel {
190 #[link(name = "Accelerate", kind = "framework")]
191 unsafe extern "C" {
192 pub fn cblas_sgemm(
193 order: i32,
194 ta: i32,
195 tb: i32,
196 m: i32,
197 n: i32,
198 k: i32,
199 alpha: f32,
200 a: *const f32,
201 lda: i32,
202 b: *const f32,
203 ldb: i32,
204 beta: f32,
205 c: *mut f32,
206 ldc: i32,
207 );
208 }
209
210 pub fn on() -> bool {
211 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
212 *ON.get_or_init(|| std::env::var("CMF_ACCEL").map(|v| v != "0").unwrap_or(true))
213 }
214}
215
216#[cfg(target_arch = "x86_64")]
218fn gemm_avx512() -> bool {
219 static ON: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
220 *ON.get_or_init(|| {
221 std::env::var("CMF_GEMM_BLOCK4")
222 .map(|v| v != "0")
223 .unwrap_or(true)
224 && std::arch::is_x86_feature_detected!("avx512f")
225 })
226}
227
228#[cfg(target_arch = "x86_64")]
233#[target_feature(enable = "avx512f")]
234unsafe fn gemm_block4x4_avx512(
235 x: &[f32],
236 w: &[f32],
237 i0: usize,
238 o0: usize,
239 k: usize,
240) -> [[f32; 4]; 4] {
241 unsafe {
243 use core::arch::x86_64::*;
244 let mut acc = [[_mm512_setzero_ps(); 4]; 4];
245 let xp = x.as_ptr().add(i0 * k);
246 let wp = w.as_ptr().add(o0 * k);
247 let mut c = 0usize;
248 while c + 16 <= k {
249 let xv = [
250 _mm512_loadu_ps(xp.add(c)),
251 _mm512_loadu_ps(xp.add(k + c)),
252 _mm512_loadu_ps(xp.add(2 * k + c)),
253 _mm512_loadu_ps(xp.add(3 * k + c)),
254 ];
255 let wv = [
256 _mm512_loadu_ps(wp.add(c)),
257 _mm512_loadu_ps(wp.add(k + c)),
258 _mm512_loadu_ps(wp.add(2 * k + c)),
259 _mm512_loadu_ps(wp.add(3 * k + c)),
260 ];
261 for a in 0..4 {
262 for b in 0..4 {
263 acc[a][b] = _mm512_fmadd_ps(xv[a], wv[b], acc[a][b]);
264 }
265 }
266 c += 16;
267 }
268 let mut out = [[0f32; 4]; 4];
269 for a in 0..4 {
270 for b in 0..4 {
271 out[a][b] = _mm512_reduce_add_ps(acc[a][b]);
272 }
273 }
274 while c < k {
277 for a in 0..4 {
278 let xe = *xp.add(a * k + c);
279 for b in 0..4 {
280 out[a][b] += xe * *wp.add(b * k + c);
281 }
282 }
283 c += 1;
284 }
285 out
286 }
287}
288
289struct GemmGuard(std::time::Instant, usize, usize, usize);
305impl Drop for GemmGuard {
306 fn drop(&mut self) {
307 crate::fcd::prof::add(&crate::fcd::prof::GEMM, self.0);
308 crate::fcd::prof::gemm_shape(self.1, self.2, self.3, self.0);
309 }
310}
311fn scopeguard_gemm(t: std::time::Instant, n: usize, k: usize, m: usize) -> GemmGuard {
312 GemmGuard(t, n, k, m)
313}
314
315pub fn gemm_nt(
316 x: &[f32],
317 w: &[f32],
318 y: &mut [f32],
319 n: usize,
320 k: usize,
321 m: usize,
322 pool: Option<&Pool>,
323) {
324 let _t_gemm = std::time::Instant::now();
325 crate::fcd::prof::GEMM_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
326 let _guard = scopeguard_gemm(_t_gemm, n, k, m);
327 #[cfg(feature = "gpu")]
330 if n * k * m >= (1 << 22) && m >= 64 && crate::gpu::enabled_here() {
331 let t0 = std::time::Instant::now();
332 match crate::gpu::probe_arm(crate::gpu::OpClass::GemmNt) {
333 crate::gpu::ProbeArm::Gpu => {
334 if crate::gpu_wgpu::gemm_nt_f32(x, w, y, n, k, m) {
335 crate::gpu::probe_record(crate::gpu::OpClass::GemmNt, true, t0.elapsed());
336 return;
337 }
338 }
339 crate::gpu::ProbeArm::CpuTimed => {
340 crate::gpu::cpu_scope(|| gemm_nt_cpu(x, w, y, n, k, m, pool));
341 crate::gpu::probe_record(crate::gpu::OpClass::GemmNt, false, t0.elapsed());
342 return;
343 }
344 crate::gpu::ProbeArm::Cpu => {}
345 }
346 }
347 gemm_nt_cpu(x, w, y, n, k, m, pool)
348}
349
350pub fn gemm_nt_host(
365 x: &[f32],
366 w: &[f32],
367 y: &mut [f32],
368 n: usize,
369 k: usize,
370 m: usize,
371 pool: Option<&Pool>,
372) {
373 assert!(
374 x.len() >= n * k,
375 "gemm_nt_host: x has {} < n·k = {}",
376 x.len(),
377 n * k
378 );
379 assert!(
380 w.len() >= m * k,
381 "gemm_nt_host: w has {} < m·k = {}",
382 w.len(),
383 m * k
384 );
385 assert!(
386 y.len() >= n * m,
387 "gemm_nt_host: y has {} < n·m = {}",
388 y.len(),
389 n * m
390 );
391 gemm_nt_cpu(&x[..n * k], &w[..m * k], &mut y[..n * m], n, k, m, pool)
392}
393
394fn gemm_nt_cpu(
395 x: &[f32],
396 w: &[f32],
397 y: &mut [f32],
398 n: usize,
399 k: usize,
400 m: usize,
401 pool: Option<&Pool>,
402) {
403 debug_assert_eq!(x.len(), n * k);
404 debug_assert_eq!(w.len(), m * k);
405 debug_assert_eq!(y.len(), n * m);
406 #[cfg(target_os = "macos")]
407 if accel::on() && n * k * m >= 1 << 18 {
408 unsafe {
410 accel::cblas_sgemm(
411 101,
412 111,
413 112,
414 n as i32,
415 m as i32,
416 k as i32,
417 1.0,
418 x.as_ptr(),
419 k as i32,
420 w.as_ptr(),
421 k as i32,
422 0.0,
423 y.as_mut_ptr(),
424 m as i32,
425 );
426 }
427 return;
428 }
429 let nb = n.div_ceil(GEMM_BLOCK);
430 let block = |r0: usize, r1: usize, y: &mut [f32]| {
431 let mut o = 0usize;
443 #[cfg(target_arch = "x86_64")]
444 if gemm_avx512() {
445 while o + 4 <= m {
446 let mut i = r0;
447 while i + 4 <= r1 {
448 let acc = unsafe { gemm_block4x4_avx512(x, w, i, o, k) };
451 for (di, ai) in acc.iter().enumerate() {
452 for (dj, v) in ai.iter().enumerate() {
453 y[(i - r0 + di) * m + o + dj] = *v;
454 }
455 }
456 i += 4;
457 }
458 for i in i..r1 {
459 let xr = &x[i * k..(i + 1) * k];
460 for dj in 0..4 {
461 let wr = &w[(o + dj) * k..(o + dj + 1) * k];
462 y[(i - r0) * m + o + dj] = crate::attention::dot_f32(xr, wr);
463 }
464 }
465 o += 4;
466 }
467 }
468 for o in o..m {
469 let wr = &w[o * k..(o + 1) * k];
470 for i in r0..r1 {
471 y[(i - r0) * m + o] = crate::attention::dot_f32(&x[i * k..(i + 1) * k], wr);
472 }
473 }
474 };
475 match pool {
476 Some(p) if nb > 1 => {
477 let yp = SendMut(y.as_mut_ptr());
478 p.run(&|widx, nw| {
479 for bi in (widx..nb).step_by(nw) {
480 let (r0, r1) = (bi * GEMM_BLOCK, ((bi + 1) * GEMM_BLOCK).min(n));
481 let ys = unsafe { yp.slice(r0 * m, (r1 - r0) * m) };
483 block(r0, r1, ys);
484 }
485 });
486 }
487 _ => {
488 for bi in 0..nb {
489 let (r0, r1) = (bi * GEMM_BLOCK, ((bi + 1) * GEMM_BLOCK).min(n));
490 block(r0, r1, &mut y[r0 * m..r1 * m]);
491 }
492 }
493 }
494}
495
496pub fn gemm_dx(
498 dy: &[f32],
499 w: &[f32],
500 dx: &mut [f32],
501 n: usize,
502 k: usize,
503 m: usize,
504 pool: Option<&Pool>,
505) {
506 let _t = std::time::Instant::now();
507 crate::fcd::prof::GEMM_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
508 let _guard = scopeguard_gemm(_t, n, k, m);
509 debug_assert_eq!(dy.len(), n * m);
510 debug_assert_eq!(w.len(), m * k);
511 debug_assert_eq!(dx.len(), n * k);
512 #[cfg(feature = "gpu")]
516 if crate::gpu_wgpu::gemm_dx_f32(dy, w, dx, n, k, m) {
517 return;
518 }
519 #[cfg(target_os = "macos")]
520 if accel::on() && n * k * m >= 1 << 18 {
521 unsafe {
523 accel::cblas_sgemm(
524 101,
525 111,
526 111,
527 n as i32,
528 k as i32,
529 m as i32,
530 1.0,
531 dy.as_ptr(),
532 m as i32,
533 w.as_ptr(),
534 k as i32,
535 1.0,
536 dx.as_mut_ptr(),
537 k as i32,
538 );
539 }
540 return;
541 }
542 let nb = n.div_ceil(GEMM_BLOCK);
543 let block = |r0: usize, r1: usize, dxs: &mut [f32]| {
544 for o in 0..m {
545 let wr = &w[o * k..(o + 1) * k];
546 for i in r0..r1 {
547 let g = dy[i * m + o];
548 if g != 0.0 {
549 crate::attention::axpy_f32(&mut dxs[(i - r0) * k..(i - r0 + 1) * k], wr, g);
550 }
551 }
552 }
553 };
554 match pool {
555 Some(p) if nb > 1 => {
556 let dxp = SendMut(dx.as_mut_ptr());
557 p.run(&|widx, nw| {
558 for bi in (widx..nb).step_by(nw) {
559 let (r0, r1) = (bi * GEMM_BLOCK, ((bi + 1) * GEMM_BLOCK).min(n));
560 let dxs = unsafe { dxp.slice(r0 * k, (r1 - r0) * k) };
562 block(r0, r1, dxs);
563 }
564 });
565 }
566 _ => {
567 for bi in 0..nb {
568 let (r0, r1) = (bi * GEMM_BLOCK, ((bi + 1) * GEMM_BLOCK).min(n));
569 block(r0, r1, &mut dx[r0 * k..r1 * k]);
570 }
571 }
572 }
573}
574
575pub fn gemm_dw(
578 dy: &[f32],
579 x: &[f32],
580 dw: &mut [f32],
581 n: usize,
582 k: usize,
583 m: usize,
584 pool: Option<&Pool>,
585) {
586 let _t = std::time::Instant::now();
587 crate::fcd::prof::GEMM_CALLS.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
588 let _guard = scopeguard_gemm(_t, n, k, m);
589 debug_assert_eq!(dy.len(), n * m);
590 debug_assert_eq!(x.len(), n * k);
591 debug_assert_eq!(dw.len(), m * k);
592 #[cfg(target_os = "macos")]
593 if accel::on() && n * k * m >= 1 << 18 {
594 unsafe {
596 accel::cblas_sgemm(
597 101,
598 112,
599 111,
600 m as i32,
601 k as i32,
602 n as i32,
603 1.0,
604 dy.as_ptr(),
605 m as i32,
606 x.as_ptr(),
607 k as i32,
608 1.0,
609 dw.as_mut_ptr(),
610 k as i32,
611 );
612 }
613 return;
614 }
615 let range = |o0: usize, o1: usize, dws: &mut [f32]| {
616 let nb = n.div_ceil(GEMM_BLOCK);
618 for bi in 0..nb {
619 let (r0, r1) = (bi * GEMM_BLOCK, ((bi + 1) * GEMM_BLOCK).min(n));
620 for o in o0..o1 {
621 let dwr = &mut dws[(o - o0) * k..(o - o0 + 1) * k];
622 for i in r0..r1 {
623 let g = dy[i * m + o];
624 if g != 0.0 {
625 crate::attention::axpy_f32(dwr, &x[i * k..(i + 1) * k], g);
626 }
627 }
628 }
629 }
630 };
631 match pool {
632 Some(p) if m >= 8 => {
633 let dwp = SendMut(dw.as_mut_ptr());
634 p.run(&|widx, nw| {
635 let (o0, o1) = (widx * m / nw, (widx + 1) * m / nw);
636 if o0 < o1 {
637 let dws = unsafe { dwp.slice(o0 * k, (o1 - o0) * k) };
639 range(o0, o1, dws);
640 }
641 });
642 }
643 _ => range(0, m, dw),
644 }
645}
646
647#[inline]
650pub fn silu<F: Fp>(x: F) -> F {
651 x / (F::ONE + (-x).exp())
652}
653
654#[inline]
656pub fn silu_bwd<F: Fp>(x: F) -> F {
657 let s = F::ONE / (F::ONE + (-x).exp());
658 s * (F::ONE + x * (F::ONE - s))
659}
660
661#[inline]
665pub fn erf_as<F: Fp>(x: F) -> F {
666 let a = x.maxf(-x);
667 let t = F::ONE / (F::ONE + F::fromf(0.327_591_1) * a);
668 let poly = ((((F::fromf(1.061_405_4) * t - F::fromf(1.453_152_1)) * t
669 + F::fromf(1.421_413_8))
670 * t
671 - F::fromf(0.284_496_74))
672 * t
673 + F::fromf(0.254_829_6))
674 * t;
675 let y = F::ONE - poly * (-(a * a)).exp();
676 if x < F::ZERO { -y } else { y }
677}
678
679#[inline]
682pub fn gelu_erf<F: Fp>(x: F) -> F {
683 F::fromf(0.5) * x * (F::ONE + erf_as(x * F::fromf(std::f64::consts::FRAC_1_SQRT_2)))
684}
685
686#[inline]
688pub fn gelu_erf_bwd<F: Fp>(x: F) -> F {
689 let cdf = F::fromf(0.5) * (F::ONE + erf_as(x * F::fromf(std::f64::consts::FRAC_1_SQRT_2)));
690 let pdf = (-(x * x) * F::fromf(0.5)).exp() * F::fromf(0.398_942_280_401_432_7);
691 cdf + x * pdf
692}
693
694#[derive(Clone, Copy, Debug, PartialEq, Eq)]
698pub enum FfnAct {
699 Silu,
700 GeluErf,
702}
703
704impl FfnAct {
705 pub fn from_arch(name: &str) -> Option<Self> {
707 match name {
708 "silu" => Some(Self::Silu),
709 "gelu" => Some(Self::GeluErf),
710 _ => None,
711 }
712 }
713
714 #[inline]
715 pub fn fwd<F: Fp>(self, x: F) -> F {
716 match self {
717 Self::Silu => silu(x),
718 Self::GeluErf => gelu_erf(x),
719 }
720 }
721
722 #[inline]
723 pub fn bwd<F: Fp>(self, x: F) -> F {
724 match self {
725 Self::Silu => silu_bwd(x),
726 Self::GeluErf => gelu_erf_bwd(x),
727 }
728 }
729}
730
731pub fn rmsnorm_fwd<F: Fp>(x: &[F], w: &[F], eps: f64, gemma: bool, y: &mut [F], inv_out: &mut [F]) {
738 let d = w.len();
739 let n = x.len() / d;
740 for r in 0..n {
741 let xr = &x[r * d..(r + 1) * d];
742 let mut ss = 0f64;
743 for v in xr {
744 ss += v.f64() * v.f64();
745 }
746 let inv = F::fromf(1.0 / (ss / d as f64 + eps).sqrt());
747 inv_out[r] = inv;
748 let yr = &mut y[r * d..(r + 1) * d];
749 for j in 0..d {
750 let weff = if gemma { F::ONE + w[j] } else { w[j] };
751 yr[j] = xr[j] * inv * weff;
752 }
753 }
754}
755
756pub fn rmsnorm_bwd<F: Fp>(
762 x: &[F],
763 w: &[F],
764 inv: &[F],
765 dy: &[F],
766 gemma: bool,
767 dx: &mut [F],
768 mut dw: Option<&mut [F]>,
769) {
770 let d = w.len();
771 let n = x.len() / d;
772 for r in 0..n {
773 let xr = &x[r * d..(r + 1) * d];
774 let dyr = &dy[r * d..(r + 1) * d];
775 let iv = inv[r];
776 let mut s = 0f64;
777 for j in 0..d {
778 let weff = if gemma { F::ONE + w[j] } else { w[j] };
779 s += (dyr[j] * weff * xr[j]).f64();
780 }
781 let coef = F::fromf(s / d as f64) * iv * iv * iv;
782 let dxr = &mut dx[r * d..(r + 1) * d];
783 for j in 0..d {
784 let weff = if gemma { F::ONE + w[j] } else { w[j] };
785 dxr[j] += iv * weff * dyr[j] - xr[j] * coef;
786 }
787 if let Some(dwv) = dw.as_deref_mut() {
788 for j in 0..d {
789 dwv[j] += dyr[j] * xr[j] * iv;
790 }
791 }
792 }
793}
794
795pub fn rope_fwd<F: Fp>(x: &mut [F], position: usize, inv_freq: &[f64]) {
800 let half = inv_freq.len();
801 for (i, &freq) in inv_freq.iter().enumerate() {
802 let angle = position as f64 * freq;
803 let (sin, cos) = (F::fromf(angle.sin()), F::fromf(angle.cos()));
804 let x0 = x[i];
805 let x1 = x[i + half];
806 x[i] = x0 * cos - x1 * sin;
807 x[i + half] = x0 * sin + x1 * cos;
808 }
809}
810
811pub fn rope_bwd<F: Fp>(dy: &mut [F], position: usize, inv_freq: &[f64]) {
814 let half = inv_freq.len();
815 for (i, &freq) in inv_freq.iter().enumerate() {
816 let angle = position as f64 * freq;
817 let (sin, cos) = (F::fromf(angle.sin()), F::fromf(angle.cos()));
818 let g0 = dy[i];
819 let g1 = dy[i + half];
820 dy[i] = g0 * cos + g1 * sin;
821 dy[i + half] = -g0 * sin + g1 * cos;
822 }
823}
824
825pub fn seg_means<F: Fp>(x: &[F], t: usize, d: usize, m: usize, out: &mut [F]) {
830 for i in 0..m {
831 let (lo, hi) = (i * t / m, (i + 1) * t / m);
832 let or = &mut out[i * d..(i + 1) * d];
833 for v in or.iter_mut() {
834 *v = F::ZERO;
835 }
836 for j in lo..hi {
837 for c in 0..d {
838 or[c] += x[j * d + c];
839 }
840 }
841 let inv = F::fromf(1.0 / (hi - lo) as f64);
842 for v in or.iter_mut() {
843 *v *= inv;
844 }
845 }
846}
847
848pub fn seg_means_bwd<F: Fp>(dl: &[F], t: usize, d: usize, m: usize, dx: &mut [F]) {
851 for i in 0..m {
852 let (lo, hi) = (i * t / m, (i + 1) * t / m);
853 let inv = F::fromf(1.0 / (hi - lo) as f64);
854 let dlr = &dl[i * d..(i + 1) * d];
855 for j in lo..hi {
856 for c in 0..d {
857 dx[j * d + c] += dlr[c] * inv;
858 }
859 }
860 }
861}
862
863pub fn attn_head_fwd<F: Fp>(
868 q: &[F],
869 k: &[F],
870 v: &[F],
871 t: usize,
872 d: usize,
873 dv: usize,
874 out: &mut [F],
875) {
876 attn_head_fwd_window(q, k, v, t, d, dv, None, out)
877}
878
879#[allow(clippy::needless_range_loop, clippy::too_many_arguments)] pub fn attn_head_fwd_window<F: Fp>(
886 q: &[F],
887 k: &[F],
888 v: &[F],
889 t: usize,
890 d: usize,
891 dv: usize,
892 window: Option<usize>,
893 out: &mut [F],
894) {
895 let scale = F::fromf(1.0 / (d as f64).sqrt());
896 let mut row = vec![F::ZERO; t];
897 for ti in 0..t {
898 let first = window.map(|w| (ti + 1).saturating_sub(w)).unwrap_or(0);
899 let qr = &q[ti * d..(ti + 1) * d];
900 let mut mx = F::fromf(f64::NEG_INFINITY);
901 for j in first..=ti {
902 let s = dot(qr, &k[j * d..(j + 1) * d]) * scale;
903 row[j] = s;
904 mx = mx.maxf(s);
905 }
906 let mut den = F::ZERO;
907 for j in first..=ti {
908 row[j] = (row[j] - mx).exp();
909 den += row[j];
910 }
911 let or = &mut out[ti * dv..(ti + 1) * dv];
912 for o in or.iter_mut() {
913 *o = F::ZERO;
914 }
915 for j in first..=ti {
916 let p = row[j] / den;
917 for (o, vv) in or.iter_mut().zip(&v[j * dv..(j + 1) * dv]) {
918 *o += p * *vv;
919 }
920 }
921 }
922}
923
924#[allow(clippy::too_many_arguments)]
928pub fn attn_head_bwd<F: Fp>(
929 q: &[F],
930 k: &[F],
931 v: &[F],
932 dout: &[F],
933 t: usize,
934 d: usize,
935 dv: usize,
936 dq: &mut [F],
937 dk: &mut [F],
938 dvv: &mut [F],
939) {
940 attn_head_bwd_window(q, k, v, dout, t, d, dv, None, dq, dk, dvv)
941}
942
943#[allow(clippy::too_many_arguments, clippy::needless_range_loop)]
945pub fn attn_head_bwd_window<F: Fp>(
946 q: &[F],
947 k: &[F],
948 v: &[F],
949 dout: &[F],
950 t: usize,
951 d: usize,
952 dv: usize,
953 window: Option<usize>,
954 dq: &mut [F],
955 dk: &mut [F],
956 dvv: &mut [F],
957) {
958 let scale = F::fromf(1.0 / (d as f64).sqrt());
959 let mut row = vec![F::ZERO; t];
960 for ti in 0..t {
961 let first = window.map(|w| (ti + 1).saturating_sub(w)).unwrap_or(0);
962 let qr = &q[ti * d..(ti + 1) * d];
963 let mut mx = F::fromf(f64::NEG_INFINITY);
964 for j in first..=ti {
965 let s = dot(qr, &k[j * d..(j + 1) * d]) * scale;
966 row[j] = s;
967 mx = mx.maxf(s);
968 }
969 let mut den = F::ZERO;
970 for j in first..=ti {
971 row[j] = (row[j] - mx).exp();
972 den += row[j];
973 }
974 let dor = &dout[ti * dv..(ti + 1) * dv];
975 let mut pdp = F::ZERO;
977 let mut dp = vec![F::ZERO; ti + 1];
978 for j in first..=ti {
979 let p = row[j] / den;
980 row[j] = p; dp[j] = dot(dor, &v[j * dv..(j + 1) * dv]);
982 pdp += p * dp[j];
983 }
984 let dqr = &mut dq[ti * d..(ti + 1) * d];
985 for j in first..=ti {
986 let p = row[j];
987 for (dvo, o) in dvv[j * dv..(j + 1) * dv].iter_mut().zip(dor) {
989 *dvo += p * *o;
990 }
991 let ds = p * (dp[j] - pdp) * scale;
993 let kr = &k[j * d..(j + 1) * d];
994 for c in 0..d {
995 dqr[c] += ds * kr[c];
996 }
997 let dkr = &mut dk[j * d..(j + 1) * d];
998 for c in 0..d {
999 dkr[c] += ds * qr[c];
1000 }
1001 }
1002 }
1003}
1004
1005#[derive(Clone, Copy, Debug)]
1009pub struct NysCfg {
1010 pub m: usize,
1012 pub w: usize,
1014 pub sink: usize,
1016 pub prefill: Option<usize>,
1028}
1029
1030impl NysCfg {
1031 #[inline]
1034 pub fn prefill_len(&self, t: usize) -> usize {
1035 self.prefill.unwrap_or(t / 2).clamp(1, t)
1036 }
1037}
1038
1039const NYS_DEN_EPS: f64 = 1e-30;
1041
1042struct NysGraph<F: Fp> {
1046 m_eff: usize,
1047 tp: usize,
1050 q_l: Vec<F>,
1051 k_l: Vec<F>,
1052 mu: Vec<F>,
1053 fu: Vec<F>,
1054 e: Vec<F>,
1055 fumu: Vec<F>,
1056 far_keep: Vec<bool>,
1065 wmat: Vec<F>,
1068 c_row: Vec<F>,
1069 den: Vec<F>,
1070}
1071
1072#[inline]
1077fn nys_exact_row(ti: usize, tp: usize) -> bool {
1078 ti < tp
1079}
1080
1081#[inline]
1083fn nys_near(ti: usize, j: usize, w: usize, sink: usize) -> bool {
1084 ti - j < w || j < sink
1085}
1086
1087fn nys_graph<F: Fp>(
1092 q: &[F],
1093 k: &[F],
1094 t: usize,
1095 d: usize,
1096 cfg: &NysCfg,
1097 mu_override: Option<&[F]>,
1098) -> NysGraph<F> {
1099 let scale = 1.0 / (d as f64).sqrt();
1100 let fscale = F::fromf(scale);
1101 let tp = cfg.prefill_len(t);
1106 let m_eff = (tp / 8).clamp(4, cfg.m);
1107 let mut q_l = vec![F::ZERO; m_eff * d];
1108 let mut k_l = vec![F::ZERO; m_eff * d];
1109 seg_means(&q[..tp * d], tp, d, m_eff, &mut q_l);
1110 seg_means(&k[..tp * d], tp, d, m_eff, &mut k_l);
1111
1112 let mut au = vec![0f64; m_eff * m_eff];
1114 for i in 0..m_eff {
1115 for j in 0..m_eff {
1116 let mut s = 0f64;
1117 for c in 0..d {
1118 s += q_l[i * d + c].f64() * k_l[j * d + c].f64();
1119 }
1120 au[i * m_eff + j] = (s * scale).exp();
1121 }
1122 }
1123 let mu: Vec<F> = match mu_override {
1124 Some(m) => m.to_vec(),
1125 None => crate::nystrom::ridge_pinv(&au, m_eff)
1126 .iter()
1127 .map(|&x| F::fromf(x))
1128 .collect(),
1129 };
1130
1131 let mut fu = vec![F::ZERO; t * m_eff];
1133 for ti in 0..t {
1134 for i in 0..m_eff {
1135 fu[ti * m_eff + i] =
1136 (dot(&q[ti * d..(ti + 1) * d], &k_l[i * d..(i + 1) * d]) * fscale).exp();
1137 }
1138 }
1139 let mut e = vec![F::ZERO; m_eff * t];
1140 for i in 0..m_eff {
1141 for j in 0..t {
1142 e[i * t + j] = (dot(&q_l[i * d..(i + 1) * d], &k[j * d..(j + 1) * d]) * fscale).exp();
1143 }
1144 }
1145 let mut fumu = vec![F::ZERO; t * m_eff];
1146 matmul_nt(
1147 &fu,
1148 &transpose(&mu, m_eff, m_eff),
1151 &mut fumu,
1152 t,
1153 m_eff,
1154 m_eff,
1155 );
1156 let mut a = vec![F::ZERO; t * t];
1161 for ti in tp..t {
1162 let fr = &fumu[ti * m_eff..(ti + 1) * m_eff];
1163 let ar = &mut a[ti * t..ti * t + ti + 1]; for (i, &f) in fr.iter().enumerate() {
1165 let er = &e[i * t..i * t + ti + 1];
1166 for (av, ev) in ar.iter_mut().zip(er) {
1167 *av += f * *ev;
1168 }
1169 }
1170 }
1171
1172 let mut wmat = vec![F::ZERO; t * t];
1175 let mut c_row = vec![F::ZERO; t];
1176 let mut den = vec![F::ZERO; t];
1177 let mut far_keep = vec![false; t];
1178 let mut lg_row = vec![F::ZERO; t];
1179 for ti in 0..t {
1180 let qr = &q[ti * d..(ti + 1) * d];
1181 let mut c = F::fromf(f64::NEG_INFINITY);
1182 for j in 0..=ti {
1183 let s = dot(qr, &k[j * d..(j + 1) * d]) * fscale;
1184 lg_row[j] = s;
1185 c = c.maxf(s);
1186 }
1187 c_row[ti] = c;
1188 let emc = (-c).exp();
1189 let exact_row = nys_exact_row(ti, tp);
1190
1191 let mut far_sum = F::ZERO;
1206 if !exact_row {
1207 for j in 0..=ti {
1208 if !nys_near(ti, j, cfg.w, cfg.sink) {
1209 far_sum += a[ti * t + j];
1210 }
1211 }
1212 }
1213 let keep = !exact_row && far_sum.f64() >= 0.0;
1214 far_keep[ti] = keep;
1215
1216 let wr = &mut wmat[ti * t..(ti + 1) * t];
1217 let mut dsum = F::ZERO;
1218 for j in 0..=ti {
1219 let wv = if exact_row || nys_near(ti, j, cfg.w, cfg.sink) {
1220 (lg_row[j] - c).exp()
1221 } else if keep {
1222 a[ti * t + j] * emc
1223 } else {
1224 F::ZERO
1225 };
1226 wr[j] = wv;
1227 dsum += wv;
1228 }
1229 den[ti] = dsum.maxf(F::fromf(NYS_DEN_EPS));
1230 }
1231 NysGraph {
1232 m_eff,
1233 tp,
1234 q_l,
1235 k_l,
1236 mu,
1237 fu,
1238 e,
1239 fumu,
1240 far_keep,
1241 wmat,
1242 c_row,
1243 den,
1244 }
1245}
1246
1247fn transpose<F: Fp>(x: &[F], rows: usize, cols: usize) -> Vec<F> {
1248 let mut out = vec![F::ZERO; rows * cols];
1249 for r in 0..rows {
1250 for c in 0..cols {
1251 out[c * rows + r] = x[r * cols + c];
1252 }
1253 }
1254 out
1255}
1256
1257#[allow(clippy::too_many_arguments)]
1260pub fn nystrom_head_fwd<F: Fp>(
1261 q: &[F],
1262 k: &[F],
1263 v: &[F],
1264 t: usize,
1265 d: usize,
1266 dv: usize,
1267 cfg: &NysCfg,
1268 out: &mut [F],
1269) {
1270 if nys_degenerate(t, cfg) {
1271 attn_head_fwd(q, k, v, t, d, dv, out);
1272 return;
1273 }
1274 nystrom_head_fwd_mu(q, k, v, t, d, dv, cfg, None, out);
1275}
1276
1277#[inline]
1286fn nys_degenerate(t: usize, cfg: &NysCfg) -> bool {
1287 cfg.prefill_len(t) <= cfg.w + cfg.sink + 8
1288}
1289
1290#[doc(hidden)]
1293pub fn nystrom_mu_for_test<F: Fp>(q: &[F], k: &[F], t: usize, d: usize, cfg: &NysCfg) -> Vec<F> {
1294 nys_graph(q, k, t, d, cfg, None).mu
1295}
1296
1297#[doc(hidden)]
1300#[allow(clippy::too_many_arguments)]
1301pub fn nystrom_head_fwd_mu<F: Fp>(
1302 q: &[F],
1303 k: &[F],
1304 v: &[F],
1305 t: usize,
1306 d: usize,
1307 dv: usize,
1308 cfg: &NysCfg,
1309 mu_override: Option<&[F]>,
1310 out: &mut [F],
1311) {
1312 let g = nys_graph(q, k, t, d, cfg, mu_override);
1313 for ti in 0..t {
1314 let wr = &g.wmat[ti * t..(ti + 1) * t];
1315 let den = g.den[ti];
1316 let or = &mut out[ti * dv..(ti + 1) * dv];
1317 for o in or.iter_mut() {
1318 *o = F::ZERO;
1319 }
1320 for j in 0..=ti {
1321 let p = wr[j] / den;
1322 if p.f64() != 0.0 {
1323 for (o, vv) in or.iter_mut().zip(&v[j * dv..(j + 1) * dv]) {
1324 *o += p * *vv;
1325 }
1326 }
1327 }
1328 }
1329}
1330
1331#[allow(clippy::too_many_arguments)]
1342pub fn nystrom_head_bwd<F: Fp>(
1343 q: &[F],
1344 k: &[F],
1345 v: &[F],
1346 dout: &[F],
1347 t: usize,
1348 d: usize,
1349 dv: usize,
1350 cfg: &NysCfg,
1351 dq: &mut [F],
1352 dk: &mut [F],
1353 dvv: &mut [F],
1354) {
1355 if nys_degenerate(t, cfg) {
1356 attn_head_bwd(q, k, v, dout, t, d, dv, dq, dk, dvv);
1357 return;
1358 }
1359 nystrom_head_bwd_mu(q, k, v, dout, t, d, dv, cfg, None, dq, dk, dvv);
1360}
1361
1362#[doc(hidden)]
1364#[allow(clippy::too_many_arguments)]
1365pub fn nystrom_head_bwd_mu<F: Fp>(
1366 q: &[F],
1367 k: &[F],
1368 v: &[F],
1369 dout: &[F],
1370 t: usize,
1371 d: usize,
1372 dv: usize,
1373 cfg: &NysCfg,
1374 mu_override: Option<&[F]>,
1375 dq: &mut [F],
1376 dk: &mut [F],
1377 dvv: &mut [F],
1378) {
1379 let scale = F::fromf(1.0 / (d as f64).sqrt());
1380 let g = nys_graph(q, k, t, d, cfg, mu_override);
1381 let m_eff = g.m_eff;
1382
1383 let mut dwmat = vec![F::ZERO; t * t];
1385 let mut out_row = vec![F::ZERO; dv];
1386 for ti in 0..t {
1387 let wr = &g.wmat[ti * t..(ti + 1) * t];
1388 let den = g.den[ti];
1389 for o in out_row.iter_mut() {
1390 *o = F::ZERO;
1391 }
1392 for j in 0..=ti {
1393 let p = wr[j] / den;
1394 if p.f64() != 0.0 {
1395 for (o, vv) in out_row.iter_mut().zip(&v[j * dv..(j + 1) * dv]) {
1396 *o += p * *vv;
1397 }
1398 }
1399 }
1400 let dor = &dout[ti * dv..(ti + 1) * dv];
1401 let dwr = &mut dwmat[ti * t..(ti + 1) * t];
1402 for j in 0..=ti {
1403 let p = wr[j] / den;
1405 if p.f64() != 0.0 {
1406 for (dvo, o) in dvv[j * dv..(j + 1) * dv].iter_mut().zip(dor) {
1407 *dvo += p * *o;
1408 }
1409 }
1410 let mut s = F::ZERO;
1412 for c in 0..dv {
1413 s += dor[c] * (v[j * dv + c] - out_row[c]);
1414 }
1415 dwr[j] = s / den;
1416 }
1417 }
1418
1419 for ti in 0..t {
1422 let qr = &q[ti * d..(ti + 1) * d];
1423 let dqr_base = ti * d;
1424 for j in 0..=ti {
1425 if !(nys_exact_row(ti, g.tp) || nys_near(ti, j, cfg.w, cfg.sink)) {
1426 continue;
1427 }
1428 let dlg = dwmat[ti * t + j] * g.wmat[ti * t + j] * scale;
1429 if dlg.f64() == 0.0 {
1430 continue;
1431 }
1432 let kr = &k[j * d..(j + 1) * d];
1433 for c in 0..d {
1434 dq[dqr_base + c] += dlg * kr[c];
1435 }
1436 let dkr = &mut dk[j * d..(j + 1) * d];
1437 for c in 0..d {
1438 dkr[c] += dlg * qr[c];
1439 }
1440 }
1441 }
1442
1443 let mut da = vec![F::ZERO; t * t];
1458 for ti in g.tp..t {
1459 if !g.far_keep[ti] {
1460 continue; }
1462 let emc = (-g.c_row[ti]).exp();
1463 for j in 0..=ti {
1464 if nys_near(ti, j, cfg.w, cfg.sink) {
1465 continue;
1466 }
1467 da[ti * t + j] = dwmat[ti * t + j] * emc;
1468 }
1469 }
1470 let mut dfumu = vec![F::ZERO; t * m_eff];
1472 for ti in 0..t {
1473 let dar = &da[ti * t..ti * t + ti + 1];
1474 let dfr = &mut dfumu[ti * m_eff..(ti + 1) * m_eff];
1475 for (i, df) in dfr.iter_mut().enumerate() {
1476 let er = &g.e[i * t..i * t + ti + 1];
1477 let mut s = F::ZERO;
1478 for (av, ev) in dar.iter().zip(er) {
1479 s += *av * *ev;
1480 }
1481 *df = s;
1482 }
1483 }
1484 let mut dfu = vec![F::ZERO; t * m_eff];
1486 matmul_nt(&dfumu, &g.mu, &mut dfu, t, m_eff, m_eff);
1487 let mut de = vec![F::ZERO; m_eff * t];
1489 for ti in 0..t {
1490 let dar = &da[ti * t..ti * t + ti + 1];
1491 let fr = &g.fumu[ti * m_eff..(ti + 1) * m_eff];
1492 for (i, &f) in fr.iter().enumerate() {
1493 if f.f64() == 0.0 {
1494 continue;
1495 }
1496 let der = &mut de[i * t..i * t + ti + 1];
1497 for (dev, av) in der.iter_mut().zip(dar) {
1498 *dev += f * *av;
1499 }
1500 }
1501 }
1502 let mut dq_l = vec![F::ZERO; m_eff * d];
1505 let mut dk_l = vec![F::ZERO; m_eff * d];
1506 for ti in 0..t {
1507 let qr = &q[ti * d..(ti + 1) * d];
1508 for i in 0..m_eff {
1509 let dlg = dfu[ti * m_eff + i] * g.fu[ti * m_eff + i] * scale;
1510 if dlg.f64() == 0.0 {
1511 continue;
1512 }
1513 let klr = &g.k_l[i * d..(i + 1) * d];
1514 for c in 0..d {
1515 dq[ti * d + c] += dlg * klr[c];
1516 }
1517 let dklr = &mut dk_l[i * d..(i + 1) * d];
1518 for c in 0..d {
1519 dklr[c] += dlg * qr[c];
1520 }
1521 }
1522 }
1523 for i in 0..m_eff {
1524 let qlr = &g.q_l[i * d..(i + 1) * d];
1525 for j in 0..t {
1526 let dlg = de[i * t + j] * g.e[i * t + j] * scale;
1527 if dlg.f64() == 0.0 {
1528 continue;
1529 }
1530 let kr = &k[j * d..(j + 1) * d];
1531 let dqlr = &mut dq_l[i * d..(i + 1) * d];
1532 for c in 0..d {
1533 dqlr[c] += dlg * kr[c];
1534 }
1535 for c in 0..d {
1536 dk[j * d + c] += dlg * qlr[c];
1537 }
1538 }
1539 }
1540 let tp = g.tp;
1544 seg_means_bwd(&dq_l, tp, d, m_eff, &mut dq[..tp * d]);
1545 seg_means_bwd(&dk_l, tp, d, m_eff, &mut dk[..tp * d]);
1546}
1547
1548pub fn ce_kl_position<F: Fp>(
1556 s_logits: &[F],
1557 t_logits: &[F],
1558 target: usize,
1559 kl_w: f64,
1560 inv_n: f64,
1561 dlogits: &mut [F],
1562) -> (f64, f64) {
1563 let vsz = s_logits.len();
1564 debug_assert_eq!(t_logits.len(), vsz);
1565 let mut smax = f64::NEG_INFINITY;
1567 let mut tmax = f64::NEG_INFINITY;
1568 for i in 0..vsz {
1569 smax = smax.max(s_logits[i].f64());
1570 tmax = tmax.max(t_logits[i].f64());
1571 }
1572 let mut ssum = 0f64;
1573 let mut tsum = 0f64;
1574 for i in 0..vsz {
1575 ssum += (s_logits[i].f64() - smax).exp();
1576 tsum += (t_logits[i].f64() - tmax).exp();
1577 }
1578 let slz = smax + ssum.ln();
1579 let tlz = tmax + tsum.ln();
1580 let ce = slz - s_logits[target].f64();
1581 let mut kl = 0f64;
1582 for i in 0..vsz {
1583 let ls = s_logits[i].f64() - slz;
1584 let lt = t_logits[i].f64() - tlz;
1585 let pt = lt.exp();
1586 let ps = ls.exp();
1587 if pt > 0.0 {
1588 kl += pt * (lt - ls);
1589 }
1590 let mut gd = (1.0 - kl_w) * ps + kl_w * (ps - pt);
1591 if i == target {
1592 gd -= 1.0 - kl_w;
1593 }
1594 dlogits[i] = F::fromf(gd * inv_n);
1595 }
1596 (ce, kl)
1597}
1598
1599pub struct GdnSeqCfg<'a> {
1616 pub nv: usize,
1617 pub nk: usize,
1618 pub dk: usize,
1619 pub dv: usize,
1620 pub kk: usize,
1621 pub rms_eps: f64,
1622 pub conv: &'a [f32],
1625 pub a_log: &'a [f32],
1627 pub dt_bias: &'a [f32],
1629 pub norm: &'a [f32],
1631}
1632
1633impl GdnSeqCfg<'_> {
1634 pub fn c_dim(&self) -> usize {
1635 2 * self.nk * self.dk + self.nv * self.dv
1636 }
1637}
1638
1639#[inline]
1640fn softplus_f<F: Fp>(x: F) -> F {
1641 if x.f64() > 20.0 {
1644 x
1645 } else {
1646 F::fromf(x.f64().exp().ln_1p())
1647 }
1648}
1649
1650#[inline]
1651fn sigmoid_f<F: Fp>(x: F) -> F {
1652 F::ONE / (F::ONE + (-x).exp())
1653}
1654
1655pub fn gdn_conv_fwd<F: Fp>(
1659 raw: &[F],
1660 t: usize,
1661 c_dim: usize,
1662 kk: usize,
1663 conv: &[f32],
1664 pre: &mut [F],
1665 cq: &mut [F],
1666) {
1667 for ti in 0..t {
1668 for c in 0..c_dim {
1669 let taps = &conv[c * kk..(c + 1) * kk];
1670 let mut acc = F::ZERO;
1671 for (j, &tap) in taps.iter().enumerate() {
1672 let p = ti as isize - (kk as isize - 1) + j as isize;
1674 if p >= 0 {
1675 acc += raw[p as usize * c_dim + c] * F::fromf(tap as f64);
1676 }
1677 }
1678 pre[ti * c_dim + c] = acc;
1679 cq[ti * c_dim + c] = silu(acc);
1680 }
1681 }
1682}
1683
1684pub fn gdn_conv_bwd<F: Fp>(
1686 pre: &[F],
1687 t: usize,
1688 c_dim: usize,
1689 kk: usize,
1690 conv: &[f32],
1691 dcq: &[F],
1692 draw: &mut [F],
1693) {
1694 for ti in 0..t {
1695 for c in 0..c_dim {
1696 let g = dcq[ti * c_dim + c];
1697 if g.f64() == 0.0 {
1698 continue;
1699 }
1700 let dp = g * silu_bwd(pre[ti * c_dim + c]);
1701 let taps = &conv[c * kk..(c + 1) * kk];
1702 for (j, &tap) in taps.iter().enumerate() {
1703 let p = ti as isize - (kk as isize - 1) + j as isize;
1704 if p >= 0 {
1705 draw[p as usize * c_dim + c] += dp * F::fromf(tap as f64);
1706 }
1707 }
1708 }
1709 }
1710}
1711
1712#[inline]
1715fn gdn_inv<F: Fp>(x: &[F], extra_scale: f64) -> (F, F) {
1716 let mut n2 = F::ZERO;
1717 for v in x {
1718 n2 += *v * *v;
1719 }
1720 let n2e = n2 + F::fromf(1e-6);
1721 let inv = F::ONE / (n2e.sqrt() * F::fromf(extra_scale));
1722 (inv, n2e)
1723}
1724
1725#[allow(clippy::too_many_arguments)]
1728pub fn gdn_group_fwd<F: Fp>(
1729 cq: &[F],
1730 z: &[F],
1731 a: &[F],
1732 b: &[F],
1733 t: usize,
1734 cfg: &GdnSeqCfg,
1735 ko: usize,
1736 out: &mut [F],
1737) {
1738 let (nv, nk, dk, dv) = (cfg.nv, cfg.nk, cfg.dk, cfg.dv);
1739 let c_dim = cfg.c_dim();
1740 let kd = nk * dk;
1741 let rep = nv / nk;
1742 let vd = nv * dv;
1743 let sqdk = (dk as f64).sqrt();
1744 for hh in 0..rep {
1745 let h = ko * rep + hh;
1746 let ea = F::fromf((cfg.a_log[h] as f64).exp());
1747 let mut s = vec![F::ZERO; dk * dv];
1748 let mut kv = vec![F::ZERO; dv];
1749 let mut o = vec![F::ZERO; dv];
1750 for ti in 0..t {
1751 let qrow = &cq[ti * c_dim + ko * dk..ti * c_dim + (ko + 1) * dk];
1752 let krow = &cq[ti * c_dim + kd + ko * dk..ti * c_dim + kd + (ko + 1) * dk];
1753 let vrow = &cq[ti * c_dim + 2 * kd + h * dv..ti * c_dim + 2 * kd + (h + 1) * dv];
1754 let (invq, _) = gdn_inv(qrow, sqdk);
1755 let (invk, _) = gdn_inv(krow, 1.0);
1756 let g = (-ea * softplus_f(a[ti * nv + h] + F::fromf(cfg.dt_bias[h] as f64))).exp();
1757 let beta = sigmoid_f(b[ti * nv + h]);
1758 for x in kv.iter_mut() {
1760 *x = F::ZERO;
1761 }
1762 for di in 0..dk {
1763 let kf = krow[di] * invk;
1764 let row = &mut s[di * dv..(di + 1) * dv];
1765 for dj in 0..dv {
1766 row[dj] *= g;
1767 kv[dj] += row[dj] * kf;
1768 }
1769 }
1770 for x in o.iter_mut() {
1771 *x = F::ZERO;
1772 }
1773 for di in 0..dk {
1774 let kf = krow[di] * invk;
1775 let qf = qrow[di] * invq;
1776 let row = &mut s[di * dv..(di + 1) * dv];
1777 for dj in 0..dv {
1778 row[dj] += kf * (vrow[dj] - kv[dj]) * beta;
1779 o[dj] += qf * row[dj];
1780 }
1781 }
1782 let mut ss = 0f64;
1784 for v in &o {
1785 ss += v.f64() * v.f64();
1786 }
1787 let inv = F::fromf(1.0 / (ss / dv as f64 + cfg.rms_eps).sqrt());
1788 for dj in 0..dv {
1789 let zv = z[ti * vd + h * dv + dj];
1790 out[ti * vd + h * dv + dj] = o[dj] * inv * F::fromf(cfg.norm[dj] as f64) * silu(zv);
1791 }
1792 }
1793 }
1794}
1795
1796#[allow(clippy::too_many_arguments)]
1800pub fn gdn_group_bwd<F: Fp>(
1801 cq: &[F],
1802 z: &[F],
1803 a: &[F],
1804 b: &[F],
1805 t: usize,
1806 cfg: &GdnSeqCfg,
1807 ko: usize,
1808 dout: &[F],
1809 dcq: &mut [F],
1810 dz: &mut [F],
1811 da: &mut [F],
1812 db: &mut [F],
1813) {
1814 let (nv, nk, dk, dv) = (cfg.nv, cfg.nk, cfg.dk, cfg.dv);
1815 let c_dim = cfg.c_dim();
1816 let kd = nk * dk;
1817 let rep = nv / nk;
1818 let vd = nv * dv;
1819 let sqdk = (dk as f64).sqrt();
1820 for hh in 0..rep {
1821 let h = ko * rep + hh;
1822 let ea = F::fromf((cfg.a_log[h] as f64).exp());
1823
1824 let mut s_hist = vec![F::ZERO; (t + 1) * dk * dv]; let mut kv_hist = vec![F::ZERO; t * dv];
1827 let mut o_hist = vec![F::ZERO; t * dv];
1828 let mut g_v = vec![F::ZERO; t];
1829 let mut beta_v = vec![F::ZERO; t];
1830 let mut sp_arg = vec![F::ZERO; t]; for ti in 0..t {
1832 let qrow = &cq[ti * c_dim + ko * dk..ti * c_dim + (ko + 1) * dk];
1833 let krow = &cq[ti * c_dim + kd + ko * dk..ti * c_dim + kd + (ko + 1) * dk];
1834 let vrow = &cq[ti * c_dim + 2 * kd + h * dv..ti * c_dim + 2 * kd + (h + 1) * dv];
1835 let (invq, _) = gdn_inv(qrow, sqdk);
1836 let (invk, _) = gdn_inv(krow, 1.0);
1837 let arg = a[ti * nv + h] + F::fromf(cfg.dt_bias[h] as f64);
1838 let g = (-ea * softplus_f(arg)).exp();
1839 let beta = sigmoid_f(b[ti * nv + h]);
1840 sp_arg[ti] = arg;
1841 g_v[ti] = g;
1842 beta_v[ti] = beta;
1843 let (prev, cur) = s_hist.split_at_mut((ti + 1) * dk * dv);
1844 let sp = &prev[ti * dk * dv..];
1845 let sn = &mut cur[..dk * dv];
1846 let kvr = &mut kv_hist[ti * dv..(ti + 1) * dv];
1847 for di in 0..dk {
1848 let kf = krow[di] * invk;
1849 for dj in 0..dv {
1850 let dec = sp[di * dv + dj] * g;
1851 sn[di * dv + dj] = dec;
1852 kvr[dj] += dec * kf;
1853 }
1854 }
1855 let or = &mut o_hist[ti * dv..(ti + 1) * dv];
1856 for di in 0..dk {
1857 let kf = krow[di] * invk;
1858 let qf = qrow[di] * invq;
1859 for dj in 0..dv {
1860 let sv = sn[di * dv + dj] + kf * (vrow[dj] - kvr[dj]) * beta_v[ti];
1861 sn[di * dv + dj] = sv;
1862 or[dj] += qf * sv;
1863 }
1864 }
1865 }
1866
1867 let mut ds = vec![F::ZERO; dk * dv];
1869 let mut do_o = vec![F::ZERO; dv];
1870 let mut du = vec![F::ZERO; dv];
1871 let mut dkv = vec![F::ZERO; dv];
1872 let mut dqh = vec![F::ZERO; dk];
1873 let mut dkh = vec![F::ZERO; dk];
1874 for ti in (0..t).rev() {
1875 let qrow = &cq[ti * c_dim + ko * dk..ti * c_dim + (ko + 1) * dk];
1876 let krow = &cq[ti * c_dim + kd + ko * dk..ti * c_dim + kd + (ko + 1) * dk];
1877 let vrow = &cq[ti * c_dim + 2 * kd + h * dv..ti * c_dim + 2 * kd + (h + 1) * dv];
1878 let (invq, nq2) = gdn_inv(qrow, sqdk);
1879 let (invk, nk2) = gdn_inv(krow, 1.0);
1880 let g = g_v[ti];
1881 let beta = beta_v[ti];
1882 let s_t = &s_hist[(ti + 1) * dk * dv..(ti + 2) * dk * dv];
1883 let s_prev = &s_hist[ti * dk * dv..(ti + 1) * dk * dv];
1884 let kvr = &kv_hist[ti * dv..(ti + 1) * dv];
1885 let or = &o_hist[ti * dv..(ti + 1) * dv];
1886
1887 let mut ss = 0f64;
1889 for v in or {
1890 ss += v.f64() * v.f64();
1891 }
1892 let inv = F::fromf(1.0 / (ss / dv as f64 + cfg.rms_eps).sqrt());
1893 let dofr = &dout[ti * vd + h * dv..ti * vd + (h + 1) * dv];
1894 let mut sdot = 0f64; for dj in 0..dv {
1897 let zv = z[ti * vd + h * dv + dj];
1898 let w = F::fromf(cfg.norm[dj] as f64);
1899 let weff = w * silu(zv);
1900 sdot += (dofr[dj] * weff * or[dj]).f64();
1901 dz[ti * vd + h * dv + dj] += dofr[dj] * or[dj] * inv * w * silu_bwd(zv);
1902 }
1903 let coef = F::fromf(sdot / dv as f64) * inv * inv * inv;
1904 for dj in 0..dv {
1905 let zv = z[ti * vd + h * dv + dj];
1906 let weff = F::fromf(cfg.norm[dj] as f64) * silu(zv);
1907 do_o[dj] = inv * weff * dofr[dj] - or[dj] * coef;
1908 }
1909
1910 for x in dqh.iter_mut() {
1912 *x = F::ZERO;
1913 }
1914 for di in 0..dk {
1915 let qf = qrow[di] * invq;
1916 let row = &s_t[di * dv..(di + 1) * dv];
1917 let dsr = &mut ds[di * dv..(di + 1) * dv];
1918 let mut acc = F::ZERO;
1919 for dj in 0..dv {
1920 dsr[dj] += qf * do_o[dj];
1921 acc += row[dj] * do_o[dj];
1922 }
1923 dqh[di] = acc;
1924 }
1925
1926 for x in du.iter_mut() {
1929 *x = F::ZERO;
1930 }
1931 for x in dkh.iter_mut() {
1932 *x = F::ZERO;
1933 }
1934 for di in 0..dk {
1935 let kf = krow[di] * invk;
1936 let dsr = &ds[di * dv..(di + 1) * dv];
1937 let mut acc = F::ZERO;
1938 for dj in 0..dv {
1939 du[dj] += dsr[dj] * kf;
1940 acc += dsr[dj] * (vrow[dj] - kvr[dj]) * beta;
1941 }
1942 dkh[di] = acc;
1943 }
1944 let mut dbeta = F::ZERO;
1945 for dj in 0..dv {
1946 dbeta += du[dj] * (vrow[dj] - kvr[dj]);
1947 dcq[ti * c_dim + 2 * kd + h * dv + dj] += beta * du[dj];
1949 dkv[dj] = -(beta * du[dj]);
1950 }
1951
1952 let mut dg = F::ZERO;
1957 for di in 0..dk {
1958 let kf = krow[di] * invk;
1959 let spr = &s_prev[di * dv..(di + 1) * dv];
1960 let dsr = &mut ds[di * dv..(di + 1) * dv];
1961 let mut acc = F::ZERO;
1962 for dj in 0..dv {
1963 let dspre = dsr[dj] + kf * dkv[dj];
1964 acc += (spr[dj] * g) * dkv[dj];
1965 dg += dspre * spr[dj];
1966 dsr[dj] = g * dspre;
1967 }
1968 dkh[di] += acc;
1969 }
1970
1971 let sig = sigmoid_f(sp_arg[ti]);
1973 da[ti * nv + h] += dg * g * (-ea) * sig;
1974 db[ti * nv + h] += dbeta * beta * (F::ONE - beta);
1975
1976 let mut qdot = F::ZERO;
1980 let mut kdot = F::ZERO;
1981 for di in 0..dk {
1982 qdot += dqh[di] * qrow[di];
1983 kdot += dkh[di] * krow[di];
1984 }
1985 for di in 0..dk {
1986 dcq[ti * c_dim + ko * dk + di] += invq * dqh[di] - qrow[di] * qdot * invq / nq2;
1987 dcq[ti * c_dim + kd + ko * dk + di] +=
1988 invk * dkh[di] - krow[di] * kdot * invk / nk2;
1989 }
1990 }
1991 }
1992}
1993
1994pub fn gdn_seq_fwd<F: Fp>(
1998 qkv: &[F],
1999 z: &[F],
2000 a: &[F],
2001 b: &[F],
2002 t: usize,
2003 cfg: &GdnSeqCfg,
2004 out: &mut [F],
2005) {
2006 let c_dim = cfg.c_dim();
2007 let mut pre = vec![F::ZERO; t * c_dim];
2008 let mut cq = vec![F::ZERO; t * c_dim];
2009 gdn_conv_fwd(qkv, t, c_dim, cfg.kk, cfg.conv, &mut pre, &mut cq);
2010 for ko in 0..cfg.nk {
2011 gdn_group_fwd(&cq, z, a, b, t, cfg, ko, out);
2012 }
2013}
2014
2015#[allow(clippy::too_many_arguments)]
2018pub fn gdn_seq_bwd<F: Fp>(
2019 qkv: &[F],
2020 z: &[F],
2021 a: &[F],
2022 b: &[F],
2023 t: usize,
2024 cfg: &GdnSeqCfg,
2025 dout: &[F],
2026 dqkv: &mut [F],
2027 dz: &mut [F],
2028 da: &mut [F],
2029 db: &mut [F],
2030) {
2031 let c_dim = cfg.c_dim();
2032 let mut pre = vec![F::ZERO; t * c_dim];
2033 let mut cq = vec![F::ZERO; t * c_dim];
2034 gdn_conv_fwd(qkv, t, c_dim, cfg.kk, cfg.conv, &mut pre, &mut cq);
2035 let mut dcq = vec![F::ZERO; t * c_dim];
2036 for ko in 0..cfg.nk {
2037 gdn_group_bwd(&cq, z, a, b, t, cfg, ko, dout, &mut dcq, dz, da, db);
2038 }
2039 gdn_conv_bwd(&pre, t, c_dim, cfg.kk, cfg.conv, &dcq, dqkv);
2040}
2041
2042#[cfg(test)]
2043mod gpu_bake_tests {
2044 #[test]
2048 #[cfg(feature = "gpu")]
2049 fn gemm_dx_gpu_matches_cpu() {
2050 if std::env::var("CMF_GPU").is_err() {
2051 eprintln!("no backend requested — skip");
2052 return;
2053 }
2054 let (n, k, m) = (8usize, 512usize, 1024usize);
2055 let dy: Vec<f32> = (0..n * m)
2056 .map(|i| ((i * 37 % 101) as f32 - 50.0) / 50.0)
2057 .collect();
2058 let w: Vec<f32> = (0..m * k)
2059 .map(|i| ((i * 17 % 97) as f32 - 48.0) / 48.0)
2060 .collect();
2061 let mut want = vec![0f32; n * k];
2062 super::gemm_dx(&dy, &w, &mut want, n, k, m, None);
2063 let mut got = vec![0f32; n * k];
2064 if !crate::gpu_wgpu::gemm_dx_f32(&dy, &w, &mut got, n, k, m) {
2065 eprintln!("device declined — skip");
2066 return;
2067 }
2068 let num: f64 = want
2069 .iter()
2070 .zip(&got)
2071 .map(|(a, b)| ((a - b) as f64).powi(2))
2072 .sum();
2073 let den: f64 = want
2074 .iter()
2075 .map(|a| (*a as f64).powi(2))
2076 .sum::<f64>()
2077 .max(1e-30);
2078 let rel = (num / den).sqrt();
2079 assert!(rel < 1e-4, "gemm_dx GPU vs CPU rel {rel:e}");
2080 }
2081}