pub const E8_DIMENSION: usize = 8;
pub const E8_NUM_ROOTS: usize = 240;
pub const E8_CODEBOOK_SIZE: usize = 1024; pub const E8_NUM_CENTROIDS: usize = E8_CODEBOOK_SIZE / E8_DIMENSION;
#[repr(C, align(64))]
#[derive(Debug, Clone, PartialEq)]
pub struct E8LatticeCodebook {
pub table: [i8; E8_CODEBOOK_SIZE],
}
const _: () = assert!(
std::mem::size_of::<E8LatticeCodebook>() == E8_CODEBOOK_SIZE,
"E8LatticeCodebook must measure exactly 1024 bytes (1 KiB)"
);
impl Default for E8LatticeCodebook {
fn default() -> Self {
Self::new()
}
}
impl E8LatticeCodebook {
pub fn new() -> Self {
let mut table = [0i8; E8_CODEBOOK_SIZE];
let mut idx = 0;
for c in 0..E8_NUM_CENTROIDS {
let mut parity = 0;
for d in 0..7 {
let sign = if ((c >> d) & 1) == 0 { 1i8 } else { -1i8 };
if sign == -1 {
parity ^= 1;
}
table[idx] = sign;
idx += 1;
}
table[idx] = if parity == 0 { 1i8 } else { -1i8 };
idx += 1;
}
Self { table }
}
#[inline(always)]
pub fn decode_nibble(nibble: u8) -> (f32, f32) {
let q0 = (nibble & 0x03) as f32;
let q1 = ((nibble >> 2) & 0x03) as f32;
let w0 = q0 - 1.5;
let w1 = q1 - 1.5;
(w0, w1)
}
#[inline(always)]
pub fn decode_4_nibbles(nibbles: &[u8; 4], scale: f32) -> [f32; E8_DIMENSION] {
let mut out = [0.0f32; E8_DIMENSION];
for k in 0..4 {
let (w0, w1) = Self::decode_nibble(nibbles[k]);
out[2 * k] = w0 * scale;
out[2 * k + 1] = w1 * scale;
}
out
}
pub fn project_and_encode_to_nibbles(x: &[f32; E8_DIMENSION], scale: f32) -> [u8; 4] {
let inv_scale = if scale > 1e-7 { 1.0 / scale } else { 1.0 };
let mut shifted_x = [0.0f32; E8_DIMENSION];
for d in 0..E8_DIMENSION {
shifted_x[d] = x[d] * inv_scale - 0.5;
}
let p_int = E8Lattice::project_to_d8(&shifted_x);
let mut nibbles = [0u8; 4];
for k in 0..4 {
let w0 = p_int[2 * k] as f32 + 0.5;
let w1 = p_int[2 * k + 1] as f32 + 0.5;
let q0 = ((w0 + 1.5).round() as i32).clamp(0, 3) as u8;
let q1 = ((w1 + 1.5).round() as i32).clamp(0, 3) as u8;
nibbles[k] = (q0 & 0x03) | ((q1 & 0x03) << 2);
}
nibbles
}
}
pub struct E8Lattice;
impl E8Lattice {
pub fn generate_240_roots() -> [[f32; E8_DIMENSION]; E8_NUM_ROOTS] {
let mut roots = [[0.0f32; E8_DIMENSION]; E8_NUM_ROOTS];
let mut idx = 0;
for i in 0..E8_DIMENSION {
for j in (i + 1)..E8_DIMENSION {
for &s_i in &[1.0f32, -1.0f32] {
for &s_j in &[1.0f32, -1.0f32] {
let mut root = [0.0f32; E8_DIMENSION];
root[i] = s_i;
root[j] = s_j;
roots[idx] = root;
idx += 1;
}
}
}
}
assert_eq!(idx, 112, "Group 1 must contain exactly 112 roots");
for mask in 0..256u32 {
if mask.count_ones() % 2 == 0 {
let mut root = [0.0f32; E8_DIMENSION];
for d in 0..E8_DIMENSION {
let sign = if ((mask >> d) & 1) == 0 { 0.5f32 } else { -0.5f32 };
root[d] = sign;
}
roots[idx] = root;
idx += 1;
}
}
assert_eq!(idx, 240, "Total E8 minimal roots must equal exactly 240");
roots
}
pub fn project_to_d8(x: &[f32; E8_DIMENSION]) -> [i32; E8_DIMENSION] {
let mut f = [0i32; E8_DIMENSION];
let mut errors = [0.0f32; E8_DIMENSION];
let mut sum_f = 0i32;
for i in 0..E8_DIMENSION {
f[i] = x[i].round() as i32;
errors[i] = (x[i] - f[i] as f32).abs();
sum_f += f[i];
}
if sum_f % 2 != 0 {
let mut max_err_idx = 0;
let mut max_err = errors[0];
for i in 1..E8_DIMENSION {
if errors[i] > max_err {
max_err = errors[i];
max_err_idx = i;
}
}
if x[max_err_idx] >= f[max_err_idx] as f32 {
f[max_err_idx] += 1;
} else {
f[max_err_idx] -= 1;
}
}
f
}
pub fn project_to_e8(x: &[f32; E8_DIMENSION]) -> [f32; E8_DIMENSION] {
let p1_int = Self::project_to_d8(x);
let mut p1 = [0.0f32; E8_DIMENSION];
let mut dist_sq1 = 0.0f32;
for i in 0..E8_DIMENSION {
p1[i] = p1_int[i] as f32;
let diff = x[i] - p1[i];
dist_sq1 += diff * diff;
}
let mut shifted_x = [0.0f32; E8_DIMENSION];
for i in 0..E8_DIMENSION {
shifted_x[i] = x[i] - 0.5;
}
let p2_int = Self::project_to_d8(&shifted_x);
let mut p2 = [0.0f32; E8_DIMENSION];
let mut dist_sq2 = 0.0f32;
for i in 0..E8_DIMENSION {
p2[i] = p2_int[i] as f32 + 0.5;
let diff = x[i] - p2[i];
dist_sq2 += diff * diff;
}
if dist_sq1 <= dist_sq2 {
p1
} else {
p2
}
}
}