use crate::guard::dense_matmul;
use crate::svd::truncated_svd;
pub fn sparse_skip_matmul(x: &[f32], w: &[f32], m: usize, k: usize, n: usize) -> Vec<f32> {
let mut o = vec![0f32; m * n];
for i in 0..m {
let orow = &mut o[i * n..(i + 1) * n];
for p in 0..k {
let xv = x[i * k + p];
if xv == 0.0 {
continue;
}
let wrow = &w[p * n..(p + 1) * n];
for j in 0..n {
orow[j] += xv * wrow[j];
}
}
}
o
}
pub fn factorize(w: &[f32], k: usize, n: usize, r: usize) -> (Vec<f32>, Vec<f32>, usize) {
let r = r.min(k).min(n).max(1);
let mut s = 0x2545_F491_4F6C_DD1Du64;
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s as i64 as f64 / i64::MAX as f64) as f32
};
let omega: Vec<f32> = (0..n * r).map(|_| next()).collect();
let mut u = vec![0f32; k * r];
for i in 0..k {
for c in 0..r {
let mut acc = 0f32;
for p in 0..n {
acc += w[i * n + p] * omega[p * r + c];
}
u[i * r + c] = acc;
}
}
for _ in 0..2 {
let mut z = vec![0f32; n * r]; for p in 0..n {
for c in 0..r {
let mut acc = 0f32;
for i in 0..k {
acc += w[i * n + p] * u[i * r + c];
}
z[p * r + c] = acc;
}
}
for i in 0..k {
for c in 0..r {
let mut acc = 0f32;
for p in 0..n {
acc += w[i * n + p] * z[p * r + c];
}
u[i * r + c] = acc;
}
}
}
let ref_norm = (0..r)
.map(|c| (0..k).map(|i| u[i * r + c].powi(2)).sum::<f32>().sqrt())
.fold(0f32, f32::max)
.max(1e-20);
for c in 0..r {
let mut nrm = 0f32;
for i in 0..k {
nrm += u[i * r + c] * u[i * r + c];
}
nrm = nrm.sqrt();
if nrm < 1e-4 * ref_norm {
for i in 0..k {
u[i * r + c] = 0.0; }
continue;
}
for i in 0..k {
u[i * r + c] /= nrm;
}
for c2 in c + 1..r {
let mut dot = 0f32;
for i in 0..k {
dot += u[i * r + c] * u[i * r + c2];
}
for i in 0..k {
u[i * r + c2] -= dot * u[i * r + c];
}
}
}
let mut v = vec![0f32; r * n];
for c in 0..r {
for j in 0..n {
let mut acc = 0f32;
for i in 0..k {
acc += u[i * r + c] * w[i * n + j];
}
v[c * n + j] = acc;
}
}
(u, v, r)
}
pub fn factored_matmul(
x: &[f32],
u: &[f32],
v: &[f32],
m: usize,
k: usize,
r: usize,
n: usize,
) -> Vec<f32> {
let xu = dense_matmul(x, u, m, k, r); dense_matmul(&xu, v, m, r, n) }
pub fn quant_matmul(x: &[f32], w: &[f32], m: usize, k: usize, n: usize) -> Vec<f32> {
let mut wq = vec![0i8; k * n];
let mut scale = vec![0f32; n];
for j in 0..n {
let mut amax = 0f32;
for p in 0..k {
amax = amax.max(w[p * n + j].abs());
}
let sc = if amax < 1e-20 { 1.0 } else { amax / 127.0 };
scale[j] = sc;
for p in 0..k {
wq[p * n + j] = (w[p * n + j] / sc).round().clamp(-127.0, 127.0) as i8;
}
}
let mut o = vec![0f32; m * n];
for i in 0..m {
for j in 0..n {
let mut acc = 0f32;
for p in 0..k {
acc += x[i * k + p] * wq[p * n + j] as f32;
}
o[i * n + j] = acc * scale[j];
}
}
o
}
pub fn transpose(w: &[f32], k: usize, n: usize) -> Vec<f32> {
let mut wt = vec![0f32; n * k];
for p in 0..k {
for j in 0..n {
wt[j * k + p] = w[p * n + j];
}
}
wt
}
pub fn quantize_cols_t(w: &[f32], k: usize, n: usize) -> (Vec<i8>, Vec<f32>) {
let mut q = vec![0i8; n * k];
let mut sc = vec![0f32; n];
for j in 0..n {
let mut amax = 0f32;
for p in 0..k {
amax = amax.max(w[p * n + j].abs());
}
let s = if amax < 1e-20 { 1.0 } else { amax / 127.0 };
sc[j] = s;
for p in 0..k {
q[j * k + p] = (w[p * n + j] / s).round().clamp(-127.0, 127.0) as i8;
}
}
(q, sc)
}
#[cfg_attr(all(target_arch = "aarch64", feature = "neon"), allow(dead_code))]
fn gemv_f32_dot_scalar(x: &[f32], wt: &[f32], k: usize, n: usize, y: &mut [f32]) {
for j in 0..n {
let wr = &wt[j * k..(j + 1) * k];
let mut s = 0f32;
for p in 0..k {
s += x[p] * wr[p];
}
y[j] = s;
}
}
#[cfg_attr(all(target_arch = "aarch64", feature = "neon"), allow(dead_code))]
fn gemv_i8_dot_scalar(x: &[f32], wtq: &[i8], sc: &[f32], k: usize, n: usize, y: &mut [f32]) {
for j in 0..n {
let wr = &wtq[j * k..(j + 1) * k];
let mut s = 0f32;
for p in 0..k {
s += x[p] * wr[p] as f32;
}
y[j] = s * sc[j];
}
}
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe fn gemv_f32_dot_neon(x: &[f32], wt: &[f32], k: usize, n: usize, y: &mut [f32]) {
use std::arch::aarch64::*;
unsafe {
for j in 0..n {
let wr = wt.as_ptr().add(j * k);
let (mut a0, mut a1, mut a2, mut a3) = (
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
);
let mut p = 0;
while p + 16 <= k {
a0 = vfmaq_f32(a0, vld1q_f32(x.as_ptr().add(p)), vld1q_f32(wr.add(p)));
a1 = vfmaq_f32(
a1,
vld1q_f32(x.as_ptr().add(p + 4)),
vld1q_f32(wr.add(p + 4)),
);
a2 = vfmaq_f32(
a2,
vld1q_f32(x.as_ptr().add(p + 8)),
vld1q_f32(wr.add(p + 8)),
);
a3 = vfmaq_f32(
a3,
vld1q_f32(x.as_ptr().add(p + 12)),
vld1q_f32(wr.add(p + 12)),
);
p += 16;
}
let mut s = vaddvq_f32(vaddq_f32(vaddq_f32(a0, a1), vaddq_f32(a2, a3)));
while p < k {
s += x[p] * *wr.add(p);
p += 1;
}
y[j] = s;
}
}
}
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe fn gemv_i8_dot_neon(x: &[f32], wtq: &[i8], sc: &[f32], k: usize, n: usize, y: &mut [f32]) {
use std::arch::aarch64::*;
unsafe {
for j in 0..n {
let wr = wtq.as_ptr().add(j * k);
let (mut a0, mut a1, mut a2, mut a3) = (
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
vdupq_n_f32(0.0),
);
let mut p = 0;
while p + 16 <= k {
let w8 = vld1q_s8(wr.add(p)); let lo = vmovl_s8(vget_low_s8(w8)); let hi = vmovl_s8(vget_high_s8(w8));
let w0 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(lo))); let w1 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(lo)));
let w2 = vcvtq_f32_s32(vmovl_s16(vget_low_s16(hi)));
let w3 = vcvtq_f32_s32(vmovl_s16(vget_high_s16(hi)));
a0 = vfmaq_f32(a0, vld1q_f32(x.as_ptr().add(p)), w0);
a1 = vfmaq_f32(a1, vld1q_f32(x.as_ptr().add(p + 4)), w1);
a2 = vfmaq_f32(a2, vld1q_f32(x.as_ptr().add(p + 8)), w2);
a3 = vfmaq_f32(a3, vld1q_f32(x.as_ptr().add(p + 12)), w3);
p += 16;
}
let mut s = vaddvq_f32(vaddq_f32(vaddq_f32(a0, a1), vaddq_f32(a2, a3)));
while p < k {
s += x[p] * (*wr.add(p)) as f32;
p += 1;
}
y[j] = s * sc[j];
}
}
}
pub fn gemv_f32_dot(x: &[f32], wt: &[f32], k: usize, n: usize) -> Vec<f32> {
let mut y = vec![0f32; n];
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
gemv_f32_dot_neon(x, wt, k, n, &mut y);
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
gemv_f32_dot_scalar(x, wt, k, n, &mut y);
y
}
pub fn gemv_i8_dot(x: &[f32], wtq: &[i8], sc: &[f32], k: usize, n: usize) -> Vec<f32> {
let mut y = vec![0f32; n];
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
gemv_i8_dot_neon(x, wtq, sc, k, n, &mut y);
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
gemv_i8_dot_scalar(x, wtq, sc, k, n, &mut y);
y
}
pub fn quantize_row_i8(x: &[f32]) -> (Vec<i8>, f32) {
let amax = x.iter().fold(0f32, |a, &v| a.max(v.abs()));
let s = if amax < 1e-20 { 1.0 } else { amax / 127.0 };
(
x.iter()
.map(|&v| (v / s).round().clamp(-127.0, 127.0) as i8)
.collect(),
s,
)
}
fn gemv_i8i8_dot_scalar(
xq: &[i8],
wtq: &[i8],
sx: f32,
sc: &[f32],
k: usize,
n: usize,
y: &mut [f32],
) {
for j in 0..n {
let wr = &wtq[j * k..(j + 1) * k];
let mut s = 0i32;
for p in 0..k {
s += xq[p] as i32 * wr[p] as i32;
}
y[j] = s as f32 * sx * sc[j];
}
}
#[cfg(all(target_arch = "aarch64", feature = "dotprod"))]
#[target_feature(enable = "dotprod")]
unsafe fn gemv_i8i8_dot_dotprod(
xq: &[i8],
wtq: &[i8],
sx: f32,
sc: &[f32],
k: usize,
n: usize,
y: &mut [f32],
) {
use std::arch::aarch64::*;
use std::arch::asm;
unsafe {
for j in 0..n {
let wr = wtq.as_ptr().add(j * k);
let (mut a0, mut a1) = (vdupq_n_s32(0), vdupq_n_s32(0)); let mut p = 0;
while p + 32 <= k {
let (xa, wa) = (vld1q_s8(xq.as_ptr().add(p)), vld1q_s8(wr.add(p)));
let (xb, wb) = (vld1q_s8(xq.as_ptr().add(p + 16)), vld1q_s8(wr.add(p + 16)));
asm!("sdot {0:v}.4s, {1:v}.16b, {2:v}.16b", inout(vreg) a0, in(vreg) xa, in(vreg) wa, options(pure, nomem, nostack, preserves_flags));
asm!("sdot {0:v}.4s, {1:v}.16b, {2:v}.16b", inout(vreg) a1, in(vreg) xb, in(vreg) wb, options(pure, nomem, nostack, preserves_flags));
p += 32;
}
let mut acc = vaddq_s32(a0, a1);
while p + 16 <= k {
let (xa, wa) = (vld1q_s8(xq.as_ptr().add(p)), vld1q_s8(wr.add(p)));
asm!("sdot {0:v}.4s, {1:v}.16b, {2:v}.16b", inout(vreg) acc, in(vreg) xa, in(vreg) wa, options(pure, nomem, nostack, preserves_flags));
p += 16;
}
let mut s = vaddvq_s32(acc);
while p < k {
s += xq[p] as i32 * (*wr.add(p)) as i32;
p += 1;
}
y[j] = s as f32 * sx * sc[j];
}
}
}
pub fn gemv_i8i8_dot(xq: &[i8], wtq: &[i8], sx: f32, sc: &[f32], k: usize, n: usize) -> Vec<f32> {
let mut y = vec![0f32; n];
#[cfg(all(target_arch = "aarch64", feature = "dotprod"))]
{
if std::arch::is_aarch64_feature_detected!("dotprod") {
unsafe { gemv_i8i8_dot_dotprod(xq, wtq, sx, sc, k, n, &mut y) };
return y;
}
}
gemv_i8i8_dot_scalar(xq, wtq, sx, sc, k, n, &mut y);
y
}
pub fn matmul_f32(x: &[f32], wt: &[f32], m: usize, k: usize, n: usize) -> Vec<f32> {
let mut y = vec![0f32; m * n];
for i in 0..m {
let (xr, yr) = (&x[i * k..(i + 1) * k], &mut y[i * n..(i + 1) * n]);
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
gemv_f32_dot_neon(xr, wt, k, n, yr);
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
gemv_f32_dot_scalar(xr, wt, k, n, yr);
}
y
}
pub fn matmul_w8a16(x: &[f32], wtq: &[i8], sc: &[f32], m: usize, k: usize, n: usize) -> Vec<f32> {
let mut y = vec![0f32; m * n];
for i in 0..m {
let (xr, yr) = (&x[i * k..(i + 1) * k], &mut y[i * n..(i + 1) * n]);
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
gemv_i8_dot_neon(xr, wtq, sc, k, n, yr);
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
gemv_i8_dot_scalar(xr, wtq, sc, k, n, yr);
}
y
}
pub fn quantize_rows_i8(x: &[f32], m: usize, k: usize) -> (Vec<i8>, Vec<f32>) {
let mut q = vec![0i8; m * k];
let mut s = vec![0f32; m];
for i in 0..m {
let (qr, sr) = quantize_row_i8(&x[i * k..(i + 1) * k]);
q[i * k..(i + 1) * k].copy_from_slice(&qr);
s[i] = sr;
}
(q, s)
}
pub fn matmul_w8a8(
xq: &[i8],
sx: &[f32],
wtq: &[i8],
sc: &[f32],
m: usize,
k: usize,
n: usize,
) -> Vec<f32> {
let mut y = vec![0f32; m * n];
#[cfg(all(target_arch = "aarch64", feature = "dotprod"))]
let has_dot = std::arch::is_aarch64_feature_detected!("dotprod");
for i in 0..m {
let (xr, yr) = (&xq[i * k..(i + 1) * k], &mut y[i * n..(i + 1) * n]);
#[cfg(all(target_arch = "aarch64", feature = "dotprod"))]
{
if has_dot {
unsafe { gemv_i8i8_dot_dotprod(xr, wtq, sx[i], sc, k, n, yr) };
} else {
gemv_i8i8_dot_scalar(xr, wtq, sx[i], sc, k, n, yr);
}
}
#[cfg(not(all(target_arch = "aarch64", feature = "dotprod")))]
gemv_i8i8_dot_scalar(xr, wtq, sx[i], sc, k, n, yr);
}
y
}
pub fn rel_err(a: &[f32], b: &[f32]) -> f32 {
let num: f32 = a
.iter()
.zip(b)
.map(|(x, y)| (x - y).powi(2))
.sum::<f32>()
.sqrt();
let den: f32 = a.iter().map(|x| x * x).sum::<f32>().sqrt() + 1e-9;
num / den
}
pub fn monarch_blocks(n: usize) -> Option<usize> {
let m = (n as f64).sqrt().round() as usize;
if m * m == n && m > 0 { Some(m) } else { None }
}
pub fn monarch_project(w: &[f32], m: usize) -> (Vec<f32>, Vec<f32>) {
let n = m * m;
let mut w1 = vec![0f32; m * m * m]; let mut w2 = vec![0f32; m * m * m]; let mut blk = vec![0f32; m * m];
for l in 0..m {
for ri in 0..m {
for s in 0..m {
for j in 0..m {
blk[s * m + j] = w[(ri * m + j) * n + (l * m + s)];
}
}
let (u, sv, v) = truncated_svd(&blk, m, m, 1); for s in 0..m {
w2[l * m * m + s * m + ri] = sv[0] * u[s];
}
for j in 0..m {
w1[ri * m * m + j * m + l] = v[j];
}
}
}
(w1, w2)
}
pub fn monarch_matmul(x: &[f32], w1: &[f32], w2: &[f32], batch: usize, m: usize) -> Vec<f32> {
let n = m * m;
let mut y = vec![0f32; batch * n];
let mut t = vec![0f32; m * m]; for b in 0..batch {
for ri in 0..m {
for l in 0..m {
let mut acc = 0f32;
for j in 0..m {
acc += x[b * n + ri * m + j] * w1[ri * m * m + j * m + l];
}
t[ri * m + l] = acc;
}
}
for l in 0..m {
for s in 0..m {
let mut acc = 0f32;
for ri in 0..m {
acc += t[ri * m + l] * w2[l * m * m + s * m + ri];
}
y[b * n + l * m + s] = acc;
}
}
}
y
}
pub fn monarch_params(m: usize) -> usize {
2 * m * m * m
}
fn unfold3(x: &[f32], d: [usize; 3], mode: usize) -> (Vec<f32>, usize, usize) {
let (d0, d1, d2) = (d[0], d[1], d[2]);
let rows = d[mode];
let cols = d0 * d1 * d2 / rows;
let mut out = vec![0f32; rows * cols];
for i0 in 0..d0 {
for i1 in 0..d1 {
for i2 in 0..d2 {
let v = x[(i0 * d1 + i1) * d2 + i2];
let (r, c) = match mode {
0 => (i0, i1 * d2 + i2),
1 => (i1, i0 * d2 + i2),
_ => (i2, i0 * d1 + i1),
};
out[r * cols + c] = v;
}
}
}
(out, rows, cols)
}
pub fn tucker_hosvd(x: &[f32], dims: [usize; 3], ranks: [usize; 3]) -> (Vec<f32>, [Vec<f32>; 3]) {
let r: [usize; 3] = [
ranks[0].min(dims[0]).max(1),
ranks[1].min(dims[1]).max(1),
ranks[2].min(dims[2]).max(1),
];
let (d0, d1, d2) = (dims[0], dims[1], dims[2]);
let mut factors: [Vec<f32>; 3] = [Vec::new(), Vec::new(), Vec::new()];
for mode in 0..3 {
let (uf, rows, cols) = unfold3(x, dims, mode);
let (u, _s, _v) = truncated_svd(&uf, rows, cols, r[mode]); factors[mode] = u;
}
let mut t0 = vec![0f32; r[0] * d1 * d2];
for i0 in 0..d0 {
for j0 in 0..r[0] {
let u = factors[0][i0 * r[0] + j0];
if u == 0.0 {
continue;
}
let src = &x[i0 * d1 * d2..(i0 + 1) * d1 * d2];
let dst = &mut t0[j0 * d1 * d2..(j0 + 1) * d1 * d2];
for e in 0..d1 * d2 {
dst[e] += u * src[e];
}
}
}
let mut t1 = vec![0f32; r[0] * r[1] * d2];
for j0 in 0..r[0] {
for i1 in 0..d1 {
for j1 in 0..r[1] {
let u = factors[1][i1 * r[1] + j1];
if u == 0.0 {
continue;
}
let src = &t0[(j0 * d1 + i1) * d2..(j0 * d1 + i1 + 1) * d2];
let dst = &mut t1[(j0 * r[1] + j1) * d2..(j0 * r[1] + j1 + 1) * d2];
for i2 in 0..d2 {
dst[i2] += u * src[i2];
}
}
}
}
let mut core = vec![0f32; r[0] * r[1] * r[2]];
for a in 0..r[0] * r[1] {
for i2 in 0..d2 {
let v = t1[a * d2 + i2];
if v == 0.0 {
continue;
}
for j2 in 0..r[2] {
core[a * r[2] + j2] += v * factors[2][i2 * r[2] + j2];
}
}
}
(core, factors)
}
pub fn tucker_reconstruct(
core: &[f32],
dims: [usize; 3],
ranks: [usize; 3],
factors: &[Vec<f32>; 3],
) -> Vec<f32> {
let r = [
ranks[0].min(dims[0]).max(1),
ranks[1].min(dims[1]).max(1),
ranks[2].min(dims[2]).max(1),
];
let (d0, d1, d2) = (dims[0], dims[1], dims[2]);
let mut r0 = vec![0f32; d0 * r[1] * r[2]];
for i0 in 0..d0 {
for j0 in 0..r[0] {
let u = factors[0][i0 * r[0] + j0];
if u == 0.0 {
continue;
}
let src = &core[j0 * r[1] * r[2]..(j0 + 1) * r[1] * r[2]];
let dst = &mut r0[i0 * r[1] * r[2]..(i0 + 1) * r[1] * r[2]];
for e in 0..r[1] * r[2] {
dst[e] += u * src[e];
}
}
}
let mut r1 = vec![0f32; d0 * d1 * r[2]];
for i0 in 0..d0 {
for i1 in 0..d1 {
for j1 in 0..r[1] {
let u = factors[1][i1 * r[1] + j1];
if u == 0.0 {
continue;
}
let src = &r0[(i0 * r[1] + j1) * r[2]..(i0 * r[1] + j1 + 1) * r[2]];
let dst = &mut r1[(i0 * d1 + i1) * r[2]..(i0 * d1 + i1 + 1) * r[2]];
for j2 in 0..r[2] {
dst[j2] += u * src[j2];
}
}
}
}
let mut out = vec![0f32; d0 * d1 * d2];
for a in 0..d0 * d1 {
for j2 in 0..r[2] {
let v = r1[a * r[2] + j2];
if v == 0.0 {
continue;
}
for i2 in 0..d2 {
out[a * d2 + i2] += v * factors[2][i2 * r[2] + j2];
}
}
}
out
}
pub fn tucker_params(dims: [usize; 3], ranks: [usize; 3]) -> usize {
ranks[0] * ranks[1] * ranks[2] + dims[0] * ranks[0] + dims[1] * ranks[1] + dims[2] * ranks[2]
}
pub type TtCore = (usize, usize, usize, Vec<f32>);
pub fn tt_svd(x: &[f32], dims: &[usize], max_rank: usize) -> Vec<TtCore> {
let nd = dims.len();
let numel: usize = dims.iter().product();
let mut cores: Vec<TtCore> = Vec::with_capacity(nd);
let mut c = x.to_vec();
let mut r_prev = 1usize;
let mut elems = numel; for k in 0..nd {
let dk = dims[k];
let rows = r_prev * dk;
let cols = elems / rows;
if k == nd - 1 {
cores.push((r_prev, dk, 1, c.clone()));
break;
}
let rk = max_rank.min(rows).min(cols).max(1);
let (u, sv, v) = truncated_svd(&c, rows, cols, rk); cores.push((r_prev, dk, rk, u)); let mut cn = vec![0f32; rk * cols];
for a in 0..rk {
let s = sv[a];
for j in 0..cols {
cn[a * cols + j] = s * v[j * rk + a];
}
}
c = cn;
r_prev = rk;
elems = rk * cols;
}
cores
}
pub fn tt_reconstruct(cores: &[TtCore]) -> Vec<f32> {
let (_, d0, r1, ref g0) = cores[0];
let mut acc = g0.clone(); let mut p = d0;
let mut rank = r1;
for core in &cores[1..] {
let (rp, dk, rn, ref g) = *core;
debug_assert_eq!(rp, rank);
let mut next = vec![0f32; p * dk * rn];
for pi in 0..p {
for ik in 0..dk {
for rnn in 0..rn {
let mut s = 0f32;
for r in 0..rank {
s += acc[pi * rank + r] * g[(r * dk + ik) * rn + rnn];
}
next[(pi * dk + ik) * rn + rnn] = s;
}
}
}
acc = next;
p *= dk;
rank = rn;
}
acc }
pub fn tt_params(cores: &[TtCore]) -> usize {
cores.iter().map(|&(rp, d, rn, _)| rp * d * rn).sum()
}
#[inline]
fn dot_f32(a: &[f32], b: &[f32], n: usize) -> f32 {
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
use std::arch::aarch64::*;
let (mut acc0, mut acc1) = (vdupq_n_f32(0.0), vdupq_n_f32(0.0));
let chunks = n / 8;
for c in 0..chunks {
let p = c * 8;
acc0 = vfmaq_f32(
acc0,
vld1q_f32(a.as_ptr().add(p)),
vld1q_f32(b.as_ptr().add(p)),
);
acc1 = vfmaq_f32(
acc1,
vld1q_f32(a.as_ptr().add(p + 4)),
vld1q_f32(b.as_ptr().add(p + 4)),
);
}
let mut s = vaddvq_f32(vaddq_f32(acc0, acc1));
for i in chunks * 8..n {
s += a[i] * b[i];
}
s
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
{
(0..n).map(|i| a[i] * b[i]).sum()
}
}
#[inline]
fn axpy_corr(acc: &mut [f32], corr: f32, p: f32, v: &[f32], n: usize) {
#[cfg(all(target_arch = "aarch64", feature = "neon"))]
unsafe {
use std::arch::aarch64::*;
let (vc, vp) = (vdupq_n_f32(corr), vdupq_n_f32(p));
let chunks = n / 4;
for c in 0..chunks {
let off = c * 4;
let va = vld1q_f32(acc.as_ptr().add(off));
let vv = vld1q_f32(v.as_ptr().add(off));
vst1q_f32(
acc.as_mut_ptr().add(off),
vfmaq_f32(vmulq_f32(va, vc), vp, vv),
);
}
for i in chunks * 4..n {
acc[i] = acc[i] * corr + p * v[i];
}
}
#[cfg(not(all(target_arch = "aarch64", feature = "neon")))]
for i in 0..n {
acc[i] = acc[i] * corr + p * v[i];
}
}
pub fn attention_naive(
q: &[f32],
k: &[f32],
v: &[f32],
bh: usize,
s: usize,
d: usize,
dv: usize,
causal: bool,
scale: f32,
) -> Vec<f32> {
let mut out = vec![0f32; bh * s * dv];
let mut scores = vec![0f32; s * s]; for h in 0..bh {
let (qb, kb) = (
&q[h * s * d..(h + 1) * s * d],
&k[h * s * d..(h + 1) * s * d],
);
let (vb, ob) = (
&v[h * s * dv..(h + 1) * s * dv],
&mut out[h * s * dv..(h + 1) * s * dv],
);
for i in 0..s {
let qrow = &qb[i * d..(i + 1) * d];
let kmax = if causal { i } else { s - 1 };
for j in 0..=kmax {
scores[i * s + j] = dot_f32(qrow, &kb[j * d..(j + 1) * d], d) * scale;
}
let m = scores[i * s..i * s + kmax + 1]
.iter()
.cloned()
.fold(f32::NEG_INFINITY, f32::max);
let mut sum = 0f32;
for sj in scores[i * s..i * s + kmax + 1].iter_mut() {
let p = (*sj - m).exp();
*sj = p;
sum += p;
}
let orow = &mut ob[i * dv..(i + 1) * dv];
let inv = 1.0 / sum;
for j in 0..=kmax {
axpy_corr(
orow,
1.0,
scores[i * s + j] * inv,
&vb[j * dv..(j + 1) * dv],
dv,
);
}
}
}
out
}
pub fn attention_flash(
q: &[f32],
k: &[f32],
v: &[f32],
bh: usize,
s: usize,
d: usize,
dv: usize,
causal: bool,
scale: f32,
bq: usize,
bk: usize,
) -> Vec<f32> {
let mut out = vec![0f32; bh * s * dv];
for h in 0..bh {
let (qb, kb) = (
&q[h * s * d..(h + 1) * s * d],
&k[h * s * d..(h + 1) * s * d],
);
let (vb, ob) = (
&v[h * s * dv..(h + 1) * s * dv],
&mut out[h * s * dv..(h + 1) * s * dv],
);
let mut qi = 0;
while qi < s {
let qe = (qi + bq).min(s);
let nq = qe - qi;
let mut m = vec![f32::NEG_INFINITY; nq]; let mut l = vec![0f32; nq]; let mut acc = vec![0f32; nq * dv]; let mut kj = 0;
while kj < s {
let ke = (kj + bk).min(s);
if causal && kj > qe - 1 {
break; }
for ii in 0..nq {
let qrow = &qb[(qi + ii) * d..(qi + ii + 1) * d];
let arow = &mut acc[ii * dv..(ii + 1) * dv];
for j in kj..ke {
if causal && j > qi + ii {
continue;
}
let sij = dot_f32(qrow, &kb[j * d..(j + 1) * d], d) * scale; let vrow = &vb[j * dv..(j + 1) * dv];
if sij > m[ii] {
let corr = (m[ii] - sij).exp(); l[ii] = l[ii] * corr + 1.0; axpy_corr(arow, corr, 1.0, vrow, dv);
m[ii] = sij;
} else {
let p = (sij - m[ii]).exp();
l[ii] += p;
axpy_corr(arow, 1.0, p, vrow, dv);
}
}
}
kj = ke;
}
for ii in 0..nq {
let li = l[ii].max(1e-20);
let (arow, orow) = (
&acc[ii * dv..(ii + 1) * dv],
&mut ob[(qi + ii) * dv..(qi + ii + 1) * dv],
);
for c in 0..dv {
orow[c] = arow[c] / li;
}
}
qi = qe;
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn factorize_recovers_low_rank() {
let (k, n) = (64usize, 48usize);
let a: Vec<f32> = (0..k * 2).map(|i| (i * 7 % 13) as f32 - 6.0).collect();
let b: Vec<f32> = (0..2 * n).map(|i| (i * 5 % 11) as f32 - 5.0).collect();
let mut w = vec![0f32; k * n];
for i in 0..k {
for j in 0..n {
let mut s = 0f32;
for p in 0..2 {
s += a[i * 2 + p] * b[p * n + j];
}
w[i * n + j] = s;
}
}
let (u, v, r) = factorize(&w, k, n, 4);
let recon = dense_matmul(&u, &v, k, r, n);
assert!(
rel_err(&w, &recon) < 1e-3,
"rank-2 recon err {}",
rel_err(&w, &recon)
);
}
fn fill(n: usize, seed: u64) -> Vec<f32> {
let mut s = seed | 1;
(0..n)
.map(|_| {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s as i64 as f64 / i64::MAX as f64) as f32
})
.collect()
}
#[test]
fn monarch_operator_matches_dense() {
let m = 4;
let n = m * m;
let (w1, w2) = (fill(m * m * m, 11), fill(m * m * m, 22));
let mut dense = vec![0f32; n * n];
for l in 0..m {
for s in 0..m {
for ri in 0..m {
for j in 0..m {
dense[(ri * m + j) * n + (l * m + s)] =
w1[ri * m * m + j * m + l] * w2[l * m * m + s * m + ri];
}
}
}
}
let batch = 3;
let x = fill(batch * n, 33);
let fast = monarch_matmul(&x, &w1, &w2, batch, m);
let slow = dense_matmul(&x, &dense, batch, n, n);
assert!(
rel_err(&slow, &fast) < 1e-5,
"monarch operator err {}",
rel_err(&slow, &fast)
);
}
#[test]
fn monarch_projects_true_monarch_exactly() {
let m = 4;
let n = m * m;
let (w1, w2) = (fill(m * m * m, 44), fill(m * m * m, 55));
let mut dense = vec![0f32; n * n];
for l in 0..m {
for s in 0..m {
for ri in 0..m {
for j in 0..m {
dense[(ri * m + j) * n + (l * m + s)] =
w1[ri * m * m + j * m + l] * w2[l * m * m + s * m + ri];
}
}
}
}
let (p1, p2) = monarch_project(&dense, m);
let x = fill(2 * n, 66);
let got = monarch_matmul(&x, &p1, &p2, 2, m);
let want = dense_matmul(&x, &dense, 2, n, n);
assert!(
rel_err(&want, &got) < 1e-3,
"monarch projection err {}",
rel_err(&want, &got)
);
}
#[test]
fn tucker_recovers_low_multilinear_rank() {
let dims = [6usize, 5, 4];
let mr = [2usize, 2, 2];
let core = fill(mr[0] * mr[1] * mr[2], 7);
let f: [Vec<f32>; 3] = [
fill(dims[0] * mr[0], 8),
fill(dims[1] * mr[1], 9),
fill(dims[2] * mr[2], 10),
];
let x = tucker_reconstruct(&core, dims, mr, &f);
let (g, u) = tucker_hosvd(&x, dims, [3, 3, 3]);
let recon = tucker_reconstruct(&g, dims, [3, 3, 3], &u);
assert!(
rel_err(&x, &recon) < 1e-3,
"tucker recon err {}",
rel_err(&x, &recon)
);
}
#[test]
fn gemv_dot_kernels_match_dense() {
let (k, n) = (256usize, 192usize);
let w = fill(k * n, 71); let x = fill(k, 72);
let want = dense_matmul(&x, &w, 1, k, n);
let wt = transpose(&w, k, n);
let gf = gemv_f32_dot(&x, &wt, k, n);
assert!(
rel_err(&want, &gf) < 1e-4,
"f32 GEMV err {}",
rel_err(&want, &gf)
);
let mut gf_scalar = vec![0f32; n];
gemv_f32_dot_scalar(&x, &wt, k, n, &mut gf_scalar);
assert!(
rel_err(&gf_scalar, &gf) < 1e-5,
"neon vs scalar f32 {}",
rel_err(&gf_scalar, &gf)
);
let (wtq, sc) = quantize_cols_t(&w, k, n);
let gi = gemv_i8_dot(&x, &wtq, &sc, k, n);
assert!(
rel_err(&want, &gi) < 0.05,
"int8 W8A16 GEMV err {}",
rel_err(&want, &gi)
);
let mut scalar = vec![0f32; n];
gemv_i8_dot_scalar(&x, &wtq, &sc, k, n, &mut scalar);
assert!(
rel_err(&scalar, &gi) < 1e-5,
"neon vs scalar int8 {}",
rel_err(&scalar, &gi)
);
let (xq, sx) = quantize_row_i8(&x);
let g88 = gemv_i8i8_dot(&xq, &wtq, sx, &sc, k, n);
assert!(
rel_err(&want, &g88) < 0.08,
"int8 W8A8 GEMV err {}",
rel_err(&want, &g88)
);
let mut s88 = vec![0f32; n];
gemv_i8i8_dot_scalar(&xq, &wtq, sx, &sc, k, n, &mut s88);
assert!(
rel_err(&s88, &g88) < 1e-6,
"sdot vs scalar W8A8 {}",
rel_err(&s88, &g88)
);
}
#[test]
fn matmul_kernels_match_dense() {
let (m, k, n) = (4usize, 128usize, 96usize);
let w = fill(k * n, 81);
let x = fill(m * k, 82);
let want = dense_matmul(&x, &w, m, k, n); let wt = transpose(&w, k, n);
let (wtq, sc) = quantize_cols_t(&w, k, n);
assert!(rel_err(&want, &matmul_f32(&x, &wt, m, k, n)) < 1e-4);
let y16 = matmul_w8a16(&x, &wtq, &sc, m, k, n);
assert!(
rel_err(&want, &y16) < 0.05,
"W8A16 err {}",
rel_err(&want, &y16)
);
let (xq, sx) = quantize_rows_i8(&x, m, k);
let y8 = matmul_w8a8(&xq, &sx, &wtq, &sc, m, k, n);
assert!(
rel_err(&want, &y8) < 0.08,
"W8A8 err {}",
rel_err(&want, &y8)
);
}
#[test]
fn flash_attention_matches_naive() {
let (bh, s, d, dv) = (2usize, 20usize, 8usize, 6usize);
let q = fill(bh * s * d, 1);
let k = fill(bh * s * d, 2);
let v = fill(bh * s * dv, 3);
let scale = 1.0 / (d as f32).sqrt();
for causal in [false, true] {
let want = attention_naive(&q, &k, &v, bh, s, d, dv, causal, scale);
for (bq, bk) in [(4usize, 4usize), (7, 5), (s, s)] {
let got = attention_flash(&q, &k, &v, bh, s, d, dv, causal, scale, bq, bk);
assert!(
rel_err(&want, &got) < 1e-5,
"flash≠naive causal={causal} bq={bq} bk={bk} err {}",
rel_err(&want, &got)
);
}
}
}
#[test]
fn tt_recovers_low_bond_rank() {
let dims = [4usize, 5, 3, 4];
let cores: Vec<TtCore> = vec![
(1, 4, 2, fill(1 * 4 * 2, 1)),
(2, 5, 2, fill(2 * 5 * 2, 2)),
(2, 3, 2, fill(2 * 3 * 2, 3)),
(2, 4, 1, fill(2 * 4 * 1, 4)),
];
let x = tt_reconstruct(&cores);
let got = tt_svd(&x, &dims, 3);
let recon = tt_reconstruct(&got);
assert!(
rel_err(&x, &recon) < 1e-3,
"tt recon err {}",
rel_err(&x, &recon)
);
}
}