use crate::fast;
use g_core::{Dtype, Error, Result, Tensor};
use rayon::prelude::*;
fn check(a: &Tensor, b: &Tensor) -> Result<(usize, usize, usize)> {
if a.dtype() != Dtype::F32 || b.dtype() != Dtype::F32 {
return Err(Error::dtype("gated_scan", "f32 only"));
}
if a.rank() != 3 || b.rank() != 3 {
return Err(Error::shape("gated_scan", "expected [B, T, D]"));
}
if a.shape() != b.shape() {
return Err(Error::shape("gated_scan", "gate/input shape mismatch"));
}
Ok((a.shape()[0], a.shape()[1], a.shape()[2]))
}
pub fn gated_scan(a: &Tensor, b: &Tensor) -> Result<Tensor> {
let (bs, t, d) = check(a, b)?;
let (ao, bo);
let av = match a.as_slice_f32() {
Ok(s) => s,
Err(_) => {
ao = a.to_vec_f32()?;
&ao
}
};
let bv = match b.as_slice_f32() {
Ok(s) => s,
Err(_) => {
bo = b.to_vec_f32()?;
&bo
}
};
let mut h = vec![0f32; bs * t * d];
let step = t * d;
h.par_chunks_mut(step).enumerate().for_each(|(bi, hb)| {
let ab = &av[bi * step..bi * step + step];
let bb = &bv[bi * step..bi * step + step];
let mut state = vec![0f32; d];
for ti in 0..t {
let off = ti * d;
let (ar, br, hr) = (&ab[off..off + d], &bb[off..off + d], &mut hb[off..off + d]);
for j in 0..d {
state[j] = ar[j] * state[j] + br[j];
hr[j] = state[j];
}
}
});
Tensor::from_vec_f32(h, a.shape())
}
pub fn gated_scan_backward(a: &Tensor, h: &Tensor, gh: &Tensor) -> Result<(Tensor, Tensor)> {
let (bs, t, d) = check(a, h)?;
let av = a.to_vec_f32()?;
let hv = h.to_vec_f32()?;
let gv = gh.to_vec_f32()?;
let step = t * d;
let mut ga = vec![0f32; bs * step];
let mut gb = vec![0f32; bs * step];
ga.par_chunks_mut(step)
.zip(gb.par_chunks_mut(step))
.enumerate()
.for_each(|(bi, (gab, gbb))| {
let ab = &av[bi * step..bi * step + step];
let hb = &hv[bi * step..bi * step + step];
let ghb = &gv[bi * step..bi * step + step];
let mut carry = vec![0f32; d];
for ti in (0..t).rev() {
let off = ti * d;
for j in 0..d {
let nxt = if ti + 1 < t {
ab[off + d + j] * carry[j]
} else {
0.0
};
let g = ghb[off + j] + nxt;
carry[j] = g;
gbb[off + j] = g;
gab[off + j] = if ti > 0 { g * hb[off - d + j] } else { 0.0 };
}
}
});
Ok((
Tensor::from_vec_f32(ga, a.shape())?,
Tensor::from_vec_f32(gb, a.shape())?,
))
}
pub fn rms_norm(x: &Tensor, eps: f32) -> Result<Tensor> {
let rank = x.rank();
if rank == 0 {
return Err(Error::shape("rms_norm", "rank >= 1"));
}
let inner = x.shape()[rank - 1];
let owned;
let src = match x.as_slice_f32() {
Ok(s) => s,
Err(_) => {
owned = x.to_vec_f32()?;
&owned
}
};
let mut out = vec![0f32; x.numel()];
fast::par_rows(&mut out, inner, |r0, blk| {
for (j, row) in blk.chunks_mut(inner).enumerate() {
let s = &src[(r0 + j) * inner..(r0 + j + 1) * inner];
let ms = s.iter().map(|v| v * v).sum::<f32>() / inner as f32;
let scale = 1.0 / (ms + eps).sqrt();
for (d, &v) in row.iter_mut().zip(s) {
*d = v * scale;
}
}
});
Tensor::from_vec_f32(out, x.shape())
}
pub fn rms_norm_backward(x: &Tensor, gy: &Tensor, eps: f32) -> Result<Tensor> {
let rank = x.rank();
let inner = x.shape()[rank - 1];
let xv = x.to_vec_f32()?;
let gv = gy.to_vec_f32()?;
let mut out = vec![0f32; x.numel()];
fast::par_rows(&mut out, inner, |r0, blk| {
for (j, row) in blk.chunks_mut(inner).enumerate() {
let base = (r0 + j) * inner;
let xs = &xv[base..base + inner];
let gs = &gv[base..base + inner];
let n = inner as f32;
let ms = xs.iter().map(|v| v * v).sum::<f32>() / n;
let inv = 1.0 / (ms + eps).sqrt();
let dot = xs.iter().zip(gs).map(|(a, b)| a * b).sum::<f32>();
let c = dot * inv * inv * inv / n;
for k in 0..inner {
row[k] = gs[k] * inv - xs[k] * c;
}
}
});
Tensor::from_vec_f32(out, x.shape())
}