extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use crate::math::exp;
use crate::ops::silu;
pub fn hada_blocks(n: usize) -> (usize, usize) {
debug_assert!(
n.is_power_of_two(),
"transform width must be a power of two"
);
let bits = usize::BITS - (n - 1).leading_zeros();
let ba = 1usize << (bits / 2);
(ba, n / ba)
}
pub fn kron_apply(z: &[f32], a: &[f32], b: &[f32], ba: usize, bb: usize, out: &mut [f32]) {
debug_assert_eq!(z.len(), ba * bb);
debug_assert_eq!(a.len(), ba * ba);
debug_assert_eq!(b.len(), bb * bb);
debug_assert_eq!(out.len(), ba * bb);
let mut t = vec![0.0f32; ba * bb];
for i in 0..ba {
let zi = &z[i * bb..(i + 1) * bb];
let ai = &a[i * ba..(i + 1) * ba];
for (k, &aik) in ai.iter().enumerate() {
if aik == 0.0 {
continue;
}
let tk = &mut t[k * bb..(k + 1) * bb];
for (tkj, &zij) in tk.iter_mut().zip(zi) {
*tkj += zij * aik;
}
}
}
for k in 0..ba {
let tk = &t[k * bb..(k + 1) * bb];
let ok = &mut out[k * bb..(k + 1) * bb];
ok.fill(0.0);
for (j, &tkj) in tk.iter().enumerate() {
if tkj == 0.0 {
continue;
}
let bj = &b[j * bb..(j + 1) * bb];
for (okl, &bjl) in ok.iter_mut().zip(bj) {
*okl += tkj * bjl;
}
}
}
}
#[derive(Debug, Clone)]
pub struct HadaMlp {
pub d1: Vec<f32>,
pub d2: Vec<f32>,
pub b2: Vec<f32>,
pub d3: Vec<f32>,
pub d4: Vec<f32>,
pub w1: (Vec<f32>, Vec<f32>),
pub w2: (Vec<f32>, Vec<f32>),
pub w3: (Vec<f32>, Vec<f32>),
pub cond_v: Vec<f32>,
pub cond_u: Vec<f32>,
pub cond_rank: usize,
}
#[derive(Debug, Clone)]
pub struct HadaPerms {
pub p1: Vec<u32>,
pub p2: Vec<u32>,
}
pub fn hadamard_mlp(x: &[f32], w: &HadaMlp, perms: &HadaPerms, hada_n: usize, out: &mut [f32]) {
let d_model = x.len();
debug_assert_eq!(out.len(), d_model);
let (ba, bb) = hada_blocks(hada_n);
let r = w.cond_rank;
let mut proj = vec![0.0f32; r];
for (j, p) in proj.iter_mut().enumerate() {
let mut acc = 0.0f32;
for (i, &xi) in x.iter().enumerate() {
acc += xi * w.cond_v[i * r + j];
}
*p = acc;
}
let max = proj.iter().copied().fold(f32::NEG_INFINITY, f32::max);
let mut sum = 0.0f32;
for p in proj.iter_mut() {
*p = exp(*p - max);
sum += *p;
}
for p in proj.iter_mut() {
*p /= sum;
}
let mut cond = vec![1.0f32; hada_n];
for (j, &pj) in proj.iter().enumerate() {
if pj == 0.0 {
continue;
}
let row = &w.cond_u[j * hada_n..(j + 1) * hada_n];
for (c, &u) in cond.iter_mut().zip(row) {
*c += pj * u;
}
}
let mut z = vec![0.0f32; hada_n];
z[..d_model].copy_from_slice(x);
let mut buf = vec![0.0f32; hada_n];
for (zi, &d) in z.iter_mut().zip(&w.d1) {
*zi *= d;
}
kron_apply(&z, &w.w1.0, &w.w1.1, ba, bb, &mut buf);
permute(&buf, &perms.p1, &mut z);
for i in 0..hada_n {
z[i] = silu(w.d2[i] * cond[i] * z[i] + w.b2[i]);
}
kron_apply(&z, &w.w2.0, &w.w2.1, ba, bb, &mut buf);
permute(&buf, &perms.p2, &mut z);
for (zi, &d) in z.iter_mut().zip(&w.d3) {
*zi *= d;
}
kron_apply(&z, &w.w3.0, &w.w3.1, ba, bb, &mut buf);
for (o, (&b, &d)) in out.iter_mut().zip(buf.iter().zip(&w.d4)) {
*o = b * d;
}
}
fn permute(src: &[f32], perm: &[u32], out: &mut [f32]) {
debug_assert_eq!(src.len(), perm.len());
for (o, &p) in out.iter_mut().zip(perm) {
*o = src[p as usize];
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn block_sizes_match_upstream() {
assert_eq!(hada_blocks(1024), (32, 32));
assert_eq!(hada_blocks(512), (16, 32));
assert_eq!(hada_blocks(256), (16, 16));
}
#[test]
fn kron_matches_a_transpose_z_b() {
let (ba, bb) = (2usize, 2usize);
let z = [1.0f32, 2.0, 3.0, 4.0]; let a = [1.0f32, 2.0, 3.0, 4.0]; let b = [0.0f32, 1.0, 1.0, 0.0]; let mut out = [0.0f32; 4];
kron_apply(&z, &a, &b, ba, bb, &mut out);
assert_eq!(out, [14.0, 10.0, 20.0, 14.0]);
}
#[test]
fn permute_gathers() {
let src = [10.0f32, 20.0, 30.0];
let perm = [2u32, 0, 1];
let mut out = [0.0f32; 3];
permute(&src, &perm, &mut out);
assert_eq!(out, [30.0, 10.0, 20.0]);
}
}