use crate::quant::{QuantScheme, ScaledFormat};
#[derive(Clone, Debug)]
pub struct ResidualBlock {
pub scales: Vec<f32>,
pub codes: Vec<Vec<u8>>,
pub format: ScaledFormat,
}
fn block_scale(block: &[f32], fmt: ScaledFormat) -> f32 {
let amax = block.iter().fold(0.0f32, |m, &x| m.max(x.abs()));
let fmax = fmt
.representable_values()
.iter()
.fold(0.0f32, |m, &x| m.max(x.abs()));
(amax / fmax).max(f32::MIN_POSITIVE)
}
pub fn residual_quantize(block: &[f32], fmt: ScaledFormat, levels: usize) -> ResidualBlock {
let mut resid: Vec<f32> = block.to_vec();
let mut scales = Vec::with_capacity(levels);
let mut codes = Vec::with_capacity(levels);
for _ in 0..levels {
let s = block_scale(&resid, fmt);
let level_codes: Vec<u8> = resid.iter().map(|&v| fmt.encode(v / s)).collect();
for (r, &c) in resid.iter_mut().zip(&level_codes) {
*r -= s * fmt.decode(c);
}
scales.push(s);
codes.push(level_codes);
}
ResidualBlock {
scales,
codes,
format: fmt,
}
}
pub fn residual_dequantize(rb: &ResidualBlock) -> Vec<f32> {
let n = rb.codes.first().map(|c| c.len()).unwrap_or(0);
let mut out = vec![0.0f32; n];
for (level, codes) in rb.codes.iter().enumerate() {
let s = rb.scales[level];
for (o, &c) in out.iter_mut().zip(codes) {
*o += s * rb.format.decode(c);
}
}
out
}
pub fn residual_roundtrip(block: &[f32], fmt: ScaledFormat, levels: usize) -> Vec<f32> {
residual_dequantize(&residual_quantize(block, fmt, levels))
}
fn dequant_rows(
data: &[f32],
rows: usize,
cols: usize,
fmt: ScaledFormat,
levels: usize,
block: usize,
) -> Vec<f32> {
let mut out = vec![0.0f32; rows * cols];
for r in 0..rows {
let base = r * cols;
let mut c0 = 0;
while c0 < cols {
let c1 = (c0 + block).min(cols);
let de = residual_roundtrip(&data[base + c0..base + c1], fmt, levels);
out[base + c0..base + c1].copy_from_slice(&de);
c0 = c1;
}
}
out
}
pub fn scheme_roundtrip_rows(
scheme: QuantScheme,
data: &[f32],
rows: usize,
cols: usize,
) -> Option<Vec<f32>> {
let (fmt, levels, group) = scheme.mxfp4x2_config()?;
Some(dequant_rows(
data,
rows,
cols,
fmt,
levels as usize,
group as usize,
))
}
pub fn residual_matmul_ref(
a: &[f32],
b: &[f32],
m: usize,
k: usize,
n: usize,
fmt: ScaledFormat,
levels: usize,
block: usize,
) -> Vec<f32> {
let aq = dequant_rows(a, m, k, fmt, levels, block);
let mut bt = vec![0.0f32; n * k];
for i in 0..k {
for j in 0..n {
bt[j * k + i] = b[i * n + j];
}
}
let bq = dequant_rows(&bt, n, k, fmt, levels, block);
let mut c = vec![0.0f32; m * n];
for r in 0..m {
for j in 0..n {
let mut s = 0.0f32;
for i in 0..k {
s += aq[r * k + i] * bq[j * k + i];
}
c[r * n + j] = s;
}
}
c
}
#[cfg(test)]
mod tests {
use super::*;
fn rms_rel(orig: &[f32], approx: &[f32]) -> f64 {
let (mut num, mut den) = (0.0f64, 0.0f64);
for (&o, &a) in orig.iter().zip(approx) {
num += (o - a).powi(2) as f64;
den += (o as f64).powi(2);
}
(num / den.max(1e-30)).sqrt()
}
#[test]
fn residual_fp4_doubles_precision() {
let fmt = ScaledFormat::F4E2M1;
const BLOCK: usize = 32;
const BLOCKS: usize = 5_000;
let mut s = 0x1234_5678_9abc_def0u64;
let mut u = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s >> 11) as f32 / (1u64 << 53) as f32
};
let mut normal = || {
let (a, b) = (u().max(1e-7), u());
(-2.0 * a.ln()).sqrt() * (std::f32::consts::TAU * b).cos()
};
let (mut e1, mut e2) = (0.0f64, 0.0f64);
let (mut dot_ref, mut dot1, mut dot2) = (0.0f64, 0.0f64, 0.0f64);
for _ in 0..BLOCKS {
let a: Vec<f32> = (0..BLOCK).map(|_| normal()).collect();
let b: Vec<f32> = (0..BLOCK).map(|_| normal()).collect();
let a1 = residual_roundtrip(&a, fmt, 1);
let a2 = residual_roundtrip(&a, fmt, 2);
let b1 = residual_roundtrip(&b, fmt, 1);
let b2 = residual_roundtrip(&b, fmt, 2);
e1 += rms_rel(&a, &a1);
e2 += rms_rel(&a, &a2);
for i in 0..BLOCK {
dot_ref += (a[i] * b[i]) as f64;
dot1 += (a1[i] * b1[i]) as f64;
dot2 += (a2[i] * b2[i]) as f64;
}
}
e1 /= BLOCKS as f64;
e2 /= BLOCKS as f64;
let bits = |e: f64| -e.log2();
eprintln!(
"MXFP4 1-level RMS={e1:.3e} ({:.1} bits) | 2-level RMS={e2:.3e} ({:.1} bits) | {:.1}x",
bits(e1),
bits(e2),
e1 / e2
);
eprintln!(
"dot: 1-level err={:.2e} 2-level err={:.2e}",
((dot1 - dot_ref) / dot_ref).abs(),
((dot2 - dot_ref) / dot_ref).abs()
);
assert!(e1 > 5e-2, "1-level FP4 should be coarse, got {e1:e}");
assert!(
e2 < e1 / 4.0,
"2-level should be >=4x better, got {e2:e} vs {e1:e}"
);
assert!(
bits(e2) > bits(e1) + 2.0,
"2-level should add >2 mantissa bits"
);
}
#[test]
fn one_level_is_plain_mxfp4() {
let fmt = ScaledFormat::F4E2M1;
let block = [0.0, 0.3, -1.7, 4.9, -6.2, 2.1, 0.05, -3.3];
let rb = residual_quantize(&block, fmt, 1);
let de = residual_dequantize(&rb);
let s = rb.scales[0];
for (i, &v) in block.iter().enumerate() {
assert_eq!(de[i], s * fmt.quantize(v / s));
}
}
#[test]
fn mxfp4x2_matmul_beats_single_level() {
let fmt = ScaledFormat::F4E2M1;
let (m, k, n, block) = (48usize, 96usize, 48usize, 32usize);
let mut s = 0xdead_beef_cafe_babeu64;
let mut u = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s >> 11) as f32 / (1u64 << 53) as f32
};
let mut normal = || {
let (a, b) = (u().max(1e-7), u());
(-2.0 * a.ln()).sqrt() * (std::f32::consts::TAU * b).cos()
};
let a: Vec<f32> = (0..m * k).map(|_| normal()).collect();
let b: Vec<f32> = (0..k * n).map(|_| normal()).collect();
let mut cf = vec![0.0f32; m * n];
for r in 0..m {
for j in 0..n {
let mut acc = 0.0f32;
for i in 0..k {
acc += a[r * k + i] * b[i * n + j];
}
cf[r * n + j] = acc;
}
}
let c1 = residual_matmul_ref(&a, &b, m, k, n, fmt, 1, block);
let c2 = residual_matmul_ref(&a, &b, m, k, n, fmt, 2, block);
let (e1, e2) = (rms_rel(&cf, &c1), rms_rel(&cf, &c2));
eprintln!(
"MxFp4 decode-GEMM: 1-level err={e1:.2e} 2-level err={e2:.2e} ({:.1}x better)",
e1 / e2
);
assert!(
e2 < e1 / 2.0,
"2-level matmul must beat 1-level: {e2:e} vs {e1:e}"
);
}
#[test]
fn quant_scheme_mxfp4x2_roundtrips() {
let scheme = QuantScheme::MxFp4x2Block { group_size: 32 };
assert_eq!(scheme.mxfp4x2_config(), Some((ScaledFormat::F4E2M1, 2, 32)));
assert!(scheme.has_scale());
assert_eq!(scheme.to_string(), "mxfp4x2/32");
assert!(
QuantScheme::MlxMxfp4 { group_size: 32 }
.mxfp4x2_config()
.is_none()
);
let (rows, cols) = (8usize, 64usize);
let data: Vec<f32> = (0..rows * cols)
.map(|i| (i as f32 * 0.137).sin() * 3.0)
.collect();
let two = scheme_roundtrip_rows(scheme, &data, rows, cols).expect("mxfp4x2");
let one = dequant_rows(&data, rows, cols, ScaledFormat::F4E2M1, 1, 32);
let (e2, e1) = (rms_rel(&data, &two), rms_rel(&data, &one));
eprintln!("QuantScheme::MxFp4x2Block: 1-level err={e1:.2e} 2-level err={e2:.2e}");
assert!(e2 < e1 / 2.0, "MxFp4x2 must beat single-level MXFP4");
}
}