use oxibonsai_core::quant_ternary::BlockTQ2_0_g128;
use oxibonsai_kernels::softmax_simd;
use crate::error::{DitError, DitResult};
use crate::gemm::gemm_abt;
fn dequant_weight(blocks: &[BlockTQ2_0_g128], n: usize, k: usize) -> DitResult<Vec<f32>> {
let mut buf = vec![0.0f32; n * k];
BlockTQ2_0_g128::dequant(blocks, &mut buf).map_err(DitError::Gguf)?;
Ok(buf)
}
pub fn dense_matmul(
input: &[f32],
weight: &[f32],
m: usize,
n: usize,
k: usize,
) -> DitResult<Vec<f32>> {
if input.len() != m * k {
return Err(DitError::Shape(format!(
"dense_matmul input len {} != m*k {}",
input.len(),
m * k
)));
}
if weight.len() != n * k {
return Err(DitError::Shape(format!(
"dense_matmul weight len {} != n*k {}",
weight.len(),
n * k
)));
}
let mut out = vec![0.0f32; m * n];
#[cfg(all(
feature = "native-cuda",
any(target_os = "linux", target_os = "windows")
))]
{
if crate::cuda_gpu::dit_gpu_enabled()
&& crate::cuda_gpu::dense_matmul_gpu(weight, input, &mut out, m, n, k).is_ok()
{
return Ok(out);
}
}
gemm_abt(input, weight, &mut out, m, n, k);
Ok(out)
}
fn par_rows<F>(out: &mut [f32], rows: usize, width: usize, body: F)
where
F: Fn(usize, &mut [f32]) + Sync,
{
debug_assert_eq!(out.len(), rows * width);
let threads = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
.min(rows.max(1));
if threads <= 1 || rows < 8 {
for (r, chunk) in out.chunks_mut(width).enumerate() {
body(r, chunk);
}
return;
}
let per = rows.div_ceil(threads);
let body_ref = &body;
std::thread::scope(|scope| {
let mut base = 0usize;
for chunk in out.chunks_mut(per * width) {
let start = base;
let chunk_rows = chunk.len() / width;
base += chunk_rows;
scope.spawn(move || {
for r in 0..chunk_rows {
let row = &mut chunk[r * width..(r + 1) * width];
body_ref(start + r, row);
}
});
}
});
}
pub fn ternary_matmul(
blocks: &[BlockTQ2_0_g128],
input: &[f32],
m: usize,
n: usize,
k: usize,
) -> DitResult<Vec<f32>> {
if input.len() != m * k {
return Err(DitError::Shape(format!(
"ternary_matmul input len {} != m*k {}",
input.len(),
m * k
)));
}
if k % 128 != 0 {
return Err(DitError::Shape(format!(
"ternary_matmul k {k} not a multiple of 128"
)));
}
let mut out = vec![0.0f32; m * n];
#[cfg(all(feature = "metal", target_os = "macos"))]
{
if crate::gpu::dit_gpu_enabled() {
match crate::gpu::ternary_matmul_gpu(blocks, input, &mut out, m, n, k) {
Ok(()) => return Ok(out),
Err(_e) => {
}
}
}
}
#[cfg(all(
feature = "native-cuda",
any(target_os = "linux", target_os = "windows")
))]
{
if crate::cuda_gpu::dit_gpu_enabled() {
match crate::cuda_gpu::ternary_matmul_gpu(blocks, input, &mut out, m, n, k) {
Ok(()) => return Ok(out),
Err(_e) => {
}
}
}
}
let weight = dequant_weight(blocks, n, k)?;
gemm_abt(input, &weight, &mut out, m, n, k);
Ok(out)
}
pub fn layer_norm_inplace(x: &mut [f32], rows: usize, dim: usize, eps: f32) {
debug_assert_eq!(x.len(), rows * dim);
let inv_dim = 1.0f32 / dim as f32;
for r in 0..rows {
let row = &mut x[r * dim..(r + 1) * dim];
let mut mean = 0.0f32;
for &v in row.iter() {
mean += v;
}
mean *= inv_dim;
let mut var = 0.0f32;
for &v in row.iter() {
let d = v - mean;
var += d * d;
}
var *= inv_dim;
let inv_std = 1.0f32 / (var + eps).sqrt();
for v in row.iter_mut() {
*v = (*v - mean) * inv_std;
}
}
}
pub fn modulate_inplace(x: &mut [f32], rows: usize, dim: usize, shift: &[f32], scale: &[f32]) {
debug_assert_eq!(scale.len(), dim);
debug_assert_eq!(shift.len(), dim);
for r in 0..rows {
let row = &mut x[r * dim..(r + 1) * dim];
for (i, v) in row.iter_mut().enumerate() {
*v = (1.0 + scale[i]) * *v + shift[i];
}
}
}
pub fn rms_norm_heads_inplace(
x: &mut [f32],
rows: usize,
head_dim: usize,
weight: &[f32],
eps: f32,
) {
debug_assert_eq!(weight.len(), head_dim);
let inv_dim = 1.0f32 / head_dim as f32;
for r in 0..rows {
let row = &mut x[r * head_dim..(r + 1) * head_dim];
let mut ms = 0.0f32;
for &v in row.iter() {
ms += v * v;
}
ms *= inv_dim;
let inv_rms = 1.0f32 / (ms + eps).sqrt();
for (i, v) in row.iter_mut().enumerate() {
*v = weight[i] * *v * inv_rms;
}
}
}
#[inline]
pub fn silu(x: f32) -> f32 {
x / (1.0 + (-x).exp())
}
pub fn silu_inplace(x: &mut [f32]) {
for v in x.iter_mut() {
*v = silu(*v);
}
}
pub fn swiglu(x: &[f32], rows: usize, half: usize) -> Vec<f32> {
debug_assert_eq!(x.len(), rows * 2 * half);
let full = 2 * half;
let mut out = vec![0.0f32; rows * half];
for r in 0..rows {
let src = &x[r * full..r * full + full];
let dst = &mut out[r * half..(r + 1) * half];
let (gate, up) = src.split_at(half);
for i in 0..half {
dst[i] = silu(gate[i]) * up[i];
}
}
out
}
#[derive(Debug, Clone)]
pub struct RopeTables {
pub cos: Vec<f32>,
pub sin: Vec<f32>,
pub seq: usize,
pub half: usize,
}
pub fn build_rope_tables(
ids: &[f32],
seq: usize,
num_axes: usize,
axes_dims: &[u32],
theta: f32,
) -> DitResult<RopeTables> {
if ids.len() != seq * num_axes {
return Err(DitError::Shape(format!(
"build_rope_tables ids len {} != seq*num_axes {}",
ids.len(),
seq * num_axes
)));
}
if axes_dims.len() != num_axes {
return Err(DitError::Shape(format!(
"build_rope_tables axes_dims len {} != num_axes {}",
axes_dims.len(),
num_axes
)));
}
let half: usize = axes_dims.iter().map(|&d| (d / 2) as usize).sum();
let mut omegas: Vec<Vec<f32>> = Vec::with_capacity(num_axes);
for &dim in axes_dims {
let pairs = (dim / 2) as usize;
let mut om = Vec::with_capacity(pairs);
for i in 0..pairs {
let scale = (2 * i) as f32 / dim as f32;
om.push(theta.powf(-scale));
}
omegas.push(om);
}
let mut cos = vec![0.0f32; seq * half];
let mut sin = vec![0.0f32; seq * half];
for t in 0..seq {
let mut off = 0usize;
for (a, om) in omegas.iter().enumerate() {
let pos = ids[t * num_axes + a];
for (i, &omega) in om.iter().enumerate() {
let angle = pos * omega;
cos[t * half + off + i] = angle.cos();
sin[t * half + off + i] = angle.sin();
}
off += om.len();
}
}
Ok(RopeTables {
cos,
sin,
seq,
half,
})
}
pub fn apply_rope_inplace(
x: &mut [f32],
num_heads: usize,
seq: usize,
head_dim: usize,
rope: &RopeTables,
) -> DitResult<()> {
if rope.seq != seq {
return Err(DitError::Shape(format!(
"apply_rope seq {} != rope.seq {}",
seq, rope.seq
)));
}
if rope.half * 2 != head_dim {
return Err(DitError::Shape(format!(
"apply_rope head_dim {} != 2*rope.half {}",
head_dim,
2 * rope.half
)));
}
let half = rope.half;
for h in 0..num_heads {
for t in 0..seq {
let base = (h * seq + t) * head_dim;
let crow = &rope.cos[t * half..(t + 1) * half];
let srow = &rope.sin[t * half..(t + 1) * half];
let row = &mut x[base..base + head_dim];
for i in 0..half {
let real = row[2 * i];
let imag = row[2 * i + 1];
let c = crow[i];
let s = srow[i];
row[2 * i] = real * c - imag * s;
row[2 * i + 1] = imag * c + real * s;
}
}
}
Ok(())
}
pub fn joint_attention(
q: &[f32],
k: &[f32],
v: &[f32],
num_heads: usize,
seq: usize,
head_dim: usize,
) -> DitResult<Vec<f32>> {
let expect = num_heads * seq * head_dim;
if q.len() != expect || k.len() != expect || v.len() != expect {
return Err(DitError::Shape(format!(
"joint_attention q/k/v len mismatch (expect {expect})"
)));
}
#[cfg(all(feature = "metal", target_os = "macos"))]
{
if crate::gpu::dit_attn_gpu_enabled() {
match crate::gpu::joint_attention_gpu(q, k, v, num_heads, seq, head_dim) {
Ok(out) => return Ok(out),
Err(_e) => {
}
}
}
}
#[cfg(all(
feature = "native-cuda",
any(target_os = "linux", target_os = "windows")
))]
{
if crate::cuda_gpu::dit_attn_gpu_enabled() {
match crate::cuda_gpu::joint_attention_gpu(q, k, v, num_heads, seq, head_dim) {
Ok(out) => return Ok(out),
Err(_e) => {
}
}
}
}
let inner = num_heads * head_dim;
let scale = 1.0f32 / (head_dim as f32).sqrt();
let mut head_out = vec![0.0f32; num_heads * seq * head_dim];
let attend_head = |h: usize, dst: &mut [f32]| {
let head_off = h * seq * head_dim;
let mut scores = vec![0.0f32; seq];
for qi in 0..seq {
let q_row = &q[head_off + qi * head_dim..head_off + (qi + 1) * head_dim];
for (ki, score) in scores.iter_mut().enumerate() {
let k_row = &k[head_off + ki * head_dim..head_off + (ki + 1) * head_dim];
*score = crate::gemm::dot(q_row, k_row, head_dim) * scale;
}
softmax_simd(&mut scores);
let o = &mut dst[qi * head_dim..(qi + 1) * head_dim];
for d in o.iter_mut() {
*d = 0.0;
}
for (ki, &w) in scores.iter().enumerate() {
let v_row = &v[head_off + ki * head_dim..head_off + (ki + 1) * head_dim];
for d in 0..head_dim {
o[d] += w * v_row[d];
}
}
}
};
par_rows(&mut head_out, num_heads, seq * head_dim, attend_head);
let mut out = vec![0.0f32; seq * inner];
for h in 0..num_heads {
for qi in 0..seq {
let src = &head_out[(h * seq + qi) * head_dim..(h * seq + qi + 1) * head_dim];
let dst = &mut out[qi * inner + h * head_dim..qi * inner + (h + 1) * head_dim];
dst.copy_from_slice(src);
}
}
Ok(out)
}
pub fn to_heads(x: &[f32], seq: usize, num_heads: usize, head_dim: usize) -> Vec<f32> {
let inner = num_heads * head_dim;
debug_assert_eq!(x.len(), seq * inner);
let mut out = vec![0.0f32; seq * inner];
for t in 0..seq {
for h in 0..num_heads {
let src = &x[t * inner + h * head_dim..t * inner + (h + 1) * head_dim];
let dst = &mut out[(h * seq + t) * head_dim..(h * seq + t + 1) * head_dim];
dst.copy_from_slice(src);
}
}
out
}
pub fn timestep_embedding(t: f32, dim: usize) -> Vec<f32> {
let half = dim / 2;
let log10000 = (10000.0f32).ln();
let mut emb = vec![0.0f32; dim];
for i in 0..half {
let freq = (-log10000 * i as f32 / half as f32).exp();
let arg = t * freq;
emb[i] = arg.cos();
emb[half + i] = arg.sin();
}
emb
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn layer_norm_zero_mean_unit_var() {
let mut x = vec![1.0, 2.0, 3.0, 4.0];
layer_norm_inplace(&mut x, 1, 4, 1e-6);
let mean: f32 = x.iter().sum::<f32>() / 4.0;
assert!(mean.abs() < 1e-5, "mean {mean}");
let var: f32 = x.iter().map(|v| v * v).sum::<f32>() / 4.0;
assert!((var - 1.0).abs() < 1e-3, "var {var}");
}
#[test]
fn dense_matmul_identity() {
let input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut weight = vec![0.0f32; 9];
for i in 0..3 {
weight[i * 3 + i] = 1.0;
}
let out = dense_matmul(&input, &weight, 2, 3, 3).expect("matmul");
assert_eq!(out, input);
}
#[test]
fn rope_pos_zero_is_identity() {
let ids = vec![0.0f32; 4];
let tables = build_rope_tables(&ids, 1, 4, &[32, 32, 32, 32], 2000.0).expect("rope");
assert_eq!(tables.half, 64);
assert!(tables.cos.iter().all(|&c| (c - 1.0).abs() < 1e-9));
assert!(tables.sin.iter().all(|&s| s.abs() < 1e-9));
let mut x: Vec<f32> = (0..128).map(|i| i as f32).collect();
let orig = x.clone();
apply_rope_inplace(&mut x, 1, 1, 128, &tables).expect("apply");
assert_eq!(x, orig);
}
#[test]
fn swiglu_matches_manual() {
let x = vec![1.0, -1.0, 2.0, 3.0]; let out = swiglu(&x, 1, 2);
assert!((out[0] - silu(1.0) * 2.0).abs() < 1e-6);
assert!((out[1] - silu(-1.0) * 3.0).abs() < 1e-6);
}
}