use super::clifford_3bit::{
bf16_to_f32, f32_to_bf16, LLOYD_MAX_3BIT_BOUNDS, LLOYD_MAX_3BIT_CENTROIDS,
};
use crate::versor::DyadicWalshVersor;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TurboQuantBlock32 {
pub base_codes: [u8; 12],
pub residual_signs: u32,
pub base_scale: u16,
pub residual_scale: u16,
}
impl Default for TurboQuantBlock32 {
fn default() -> Self {
Self {
base_codes: [0u8; 12],
residual_signs: 0,
base_scale: 0,
residual_scale: 0,
}
}
}
impl TurboQuantBlock32 {
pub fn pack(weights: &[f32; 32]) -> Self {
let sum_sq: f32 = weights.iter().map(|&w| w * w).sum();
let std_dev = (sum_sq / 32.0).sqrt().max(1e-8);
let inv_scale = 1.0 / std_dev;
let mut base_codes = [0u8; 12];
let mut residual = [0.0f32; 32];
for g in 0..4 {
let mut g_bits: u32 = 0;
for i in 0..8 {
let idx = g * 8 + i;
let w = weights[idx];
let norm = w * inv_scale;
let sign_bit = if norm < 0.0 { 1u32 } else { 0u32 };
let abs_val = norm.abs();
let mag_code = if abs_val < LLOYD_MAX_3BIT_BOUNDS[0] {
0u32
} else if abs_val < LLOYD_MAX_3BIT_BOUNDS[1] {
1u32
} else if abs_val < LLOYD_MAX_3BIT_BOUNDS[2] {
2u32
} else {
3u32
};
let code_3bit = (sign_bit << 2) | mag_code;
g_bits |= code_3bit << (i * 3);
let recon_sign = if sign_bit == 1 { -1.0f32 } else { 1.0f32 };
let recon_base = recon_sign * LLOYD_MAX_3BIT_CENTROIDS[mag_code as usize] * std_dev;
residual[idx] = w - recon_base;
}
base_codes[g * 3] = (g_bits & 0xFF) as u8;
base_codes[g * 3 + 1] = ((g_bits >> 8) & 0xFF) as u8;
base_codes[g * 3 + 2] = ((g_bits >> 16) & 0xFF) as u8;
}
let mut residual_signs: u32 = 0;
for (i, &r) in residual.iter().enumerate() {
if r >= 0.0 {
residual_signs |= 1u32 << i;
}
}
let mut u_norm = [0.0f32; 32];
let mut v_sign = [0.0f32; 32];
for g in 0..4 {
let b0 = base_codes[g * 3] as u32;
let b1 = base_codes[g * 3 + 1] as u32;
let b2 = base_codes[g * 3 + 2] as u32;
let g_bits = b0 | (b1 << 8) | (b2 << 16);
for i in 0..8 {
let idx = g * 8 + i;
let code = ((g_bits >> (i * 3)) & 0x7) as usize;
let sign = if (code & 0x4) != 0 { -1.0f32 } else { 1.0f32 };
let mag_idx = code & 0x3;
u_norm[idx] = sign * LLOYD_MAX_3BIT_CENTROIDS[mag_idx];
let res_bit = (residual_signs >> idx) & 1;
v_sign[idx] = if res_bit == 1 { 1.0f32 } else { -1.0f32 };
}
}
let g11: f32 = u_norm.iter().map(|&u| u * u).sum();
let g12: f32 = u_norm.iter().zip(v_sign.iter()).map(|(&u, &v)| u * v).sum();
let g22: f32 = 32.0f32;
let rhs1: f32 = weights.iter().zip(u_norm.iter()).map(|(&w, &u)| w * u).sum();
let rhs2: f32 = weights.iter().zip(v_sign.iter()).map(|(&w, &v)| w * v).sum();
let det = g11 * g22 - g12 * g12;
let (alpha1, alpha2) = if det.abs() > 1e-12 {
let a1 = (rhs1 * g22 - rhs2 * g12) / det;
let a2 = (rhs2 * g11 - rhs1 * g12) / det;
(a1, a2)
} else {
(std_dev, std_dev * 0.2)
};
Self {
base_codes,
residual_signs,
base_scale: f32_to_bf16(alpha1),
residual_scale: f32_to_bf16(alpha2),
}
}
pub fn unpack(&self, out: &mut [f32; 32]) {
let b_scale = bf16_to_f32(self.base_scale);
let r_scale = bf16_to_f32(self.residual_scale);
for g in 0..4 {
let b0 = self.base_codes[g * 3] as u32;
let b1 = self.base_codes[g * 3 + 1] as u32;
let b2 = self.base_codes[g * 3 + 2] as u32;
let g_bits = b0 | (b1 << 8) | (b2 << 16);
for i in 0..8 {
let idx = g * 8 + i;
let code = ((g_bits >> (i * 3)) & 0x7) as usize;
let sign = if (code & 0x4) != 0 { -1.0f32 } else { 1.0f32 };
let mag_idx = code & 0x3;
let base_val = sign * LLOYD_MAX_3BIT_CENTROIDS[mag_idx] * b_scale;
let res_bit = (self.residual_signs >> idx) & 1;
let res_sign = if res_bit == 1 { 1.0f32 } else { -1.0f32 };
let res_val = res_sign * r_scale;
out[idx] = base_val + res_val;
}
}
}
#[inline(always)]
pub fn dot_with_slice(&self, x: &[f32; 32]) -> f32 {
let b_scale = bf16_to_f32(self.base_scale);
let r_scale = bf16_to_f32(self.residual_scale);
let mut total = 0.0f32;
for g in 0..4 {
let b0 = self.base_codes[g * 3] as u32;
let b1 = self.base_codes[g * 3 + 1] as u32;
let b2 = self.base_codes[g * 3 + 2] as u32;
let g_bits = b0 | (b1 << 8) | (b2 << 16);
for i in 0..8 {
let idx = g * 8 + i;
let code = ((g_bits >> (i * 3)) & 0x7) as usize;
let sign = if (code & 0x4) != 0 { -1.0f32 } else { 1.0f32 };
let mag_idx = code & 0x3;
let base_val = sign * LLOYD_MAX_3BIT_CENTROIDS[mag_idx] * b_scale;
let res_bit = (self.residual_signs >> idx) & 1;
let res_sign = if res_bit == 1 { 1.0f32 } else { -1.0f32 };
let res_val = res_sign * r_scale;
total += (base_val + res_val) * x[idx];
}
}
total
}
}
#[repr(C, align(64))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TurboQuantCacheLine64 {
pub blocks: [TurboQuantBlock32; 2],
pub _padding: [u8; 24], }
impl Default for TurboQuantCacheLine64 {
fn default() -> Self {
Self {
blocks: [TurboQuantBlock32::default(); 2],
_padding: [0u8; 24],
}
}
}
impl TurboQuantCacheLine64 {
pub const CAPACITY: usize = 64;
pub fn pack_64(weights: &[f32; 64]) -> Self {
let b0: &[f32; 32] = weights[0..32].try_into().unwrap();
let b1: &[f32; 32] = weights[32..64].try_into().unwrap();
Self {
blocks: [TurboQuantBlock32::pack(b0), TurboQuantBlock32::pack(b1)],
_padding: [0u8; 24],
}
}
#[inline(always)]
pub fn dot_64(&self, x: &[f32; 64]) -> f32 {
let x0: &[f32; 32] = x[0..32].try_into().unwrap();
let x1: &[f32; 32] = x[32..64].try_into().unwrap();
self.blocks[0].dot_with_slice(x0) + self.blocks[1].dot_with_slice(x1)
}
}
#[derive(Debug, Clone)]
pub struct TurboQuantTensor {
pub rows: usize,
pub cols: usize,
pub cache_lines: Vec<TurboQuantCacheLine64>,
}
impl TurboQuantTensor {
pub fn compile_aot(weights: &[f32], rows: usize, cols: usize, versor: &DyadicWalshVersor) -> Self {
assert_eq!(weights.len(), rows * cols);
assert_eq!(cols % 64, 0, "Columns must be a multiple of 64 for cache-line alignment");
assert_eq!(versor.dimension(), cols);
let lines_per_row = cols / 64;
let mut cache_lines = Vec::with_capacity(rows * lines_per_row);
let mut row_buffer = vec![0.0f32; cols];
for r in 0..rows {
let row_slice = &weights[r * cols..(r + 1) * cols];
row_buffer.copy_from_slice(row_slice);
versor.apply_in_place(&mut row_buffer);
for l in 0..lines_per_row {
let chunk: &[f32; 64] = row_buffer[l * 64..(l + 1) * 64].try_into().unwrap();
cache_lines.push(TurboQuantCacheLine64::pack_64(chunk));
}
}
Self {
rows,
cols,
cache_lines,
}
}
pub fn matvec(&self, x: &[f32], out: &mut [f32], versor: &DyadicWalshVersor) {
assert_eq!(x.len(), self.cols);
assert_eq!(out.len(), self.rows);
let mut x_rot = x.to_vec();
versor.apply_in_place(&mut x_rot);
let lines_per_row = self.cols / 64;
for r in 0..self.rows {
let mut row_dot = 0.0f32;
let line_offset = r * lines_per_row;
for l in 0..lines_per_row {
let cache_line = &self.cache_lines[line_offset + l];
let chunk_x: &[f32; 64] = x_rot[l * 64..(l + 1) * 64].try_into().unwrap();
row_dot += cache_line.dot_64(chunk_x);
}
out[r] = row_dot;
}
}
}