use half::f16;
use rayon::prelude::*;
use super::common::{make_qkx2_quants, make_qkx3_quants, make_qp_quants, nearest_int};
pub const QK_K: usize = 256;
pub const BLOCK_BYTES: usize = QK_K / 16 + QK_K / 4 + 2 + 2;
pub fn quantize(src: &[f32], n_per_row: usize, imatrix: Option<&[f32]>) -> Vec<u8> {
assert!(
n_per_row % QK_K == 0,
"n_per_row {} not multiple of QK_K {}",
n_per_row,
QK_K
);
assert!(
src.len() % n_per_row == 0,
"src len {} not multiple of n_per_row {}",
src.len(),
n_per_row
);
if let Some(qw) = imatrix {
assert_eq!(
qw.len(),
n_per_row,
"imatrix len {} must equal n_per_row {} (dispatcher reuses pointer per row)",
qw.len(),
n_per_row
);
}
let n_rows = src.len() / n_per_row;
let row_blocks = n_per_row / QK_K;
let row_bytes = row_blocks * BLOCK_BYTES;
let mut out = vec![0u8; n_rows * row_bytes];
out.par_chunks_exact_mut(row_bytes)
.enumerate()
.for_each(|(row, dst)| {
let row_x = &src[row * n_per_row..(row + 1) * n_per_row];
let mut tmp = Vec::with_capacity(row_bytes);
match imatrix {
None => quantize_row_ref(row_x, &mut tmp),
Some(qw) => quantize_row_impl(row_x, qw, &mut tmp),
}
debug_assert_eq!(tmp.len(), row_bytes);
dst.copy_from_slice(&tmp);
});
out
}
fn quantize_row_ref(x: &[f32], out: &mut Vec<u8>) {
debug_assert_eq!(x.len() % QK_K, 0);
let nb = x.len() / QK_K;
let q4scale = 15.0f32;
let mut l_buf = [0u8; QK_K];
let mut l_aux = [0u8; 16];
let mut weights = [0.0f32; 16];
let mut mins = [0.0f32; QK_K / 16];
let mut scales = [0.0f32; QK_K / 16];
for i in 0..nb {
let xb = &x[QK_K * i..QK_K * (i + 1)];
let mut max_scale = 0.0f32;
let mut max_min = 0.0f32;
for j in 0..QK_K / 16 {
for l in 0..16 {
weights[l] = xb[16 * j + l].abs();
}
let mut mn = 0.0f32;
let sc = make_qkx2_quants(
16,
3,
&xb[16 * j..16 * j + 16],
&weights,
&mut l_buf[16 * j..16 * j + 16],
&mut mn,
&mut l_aux,
-0.5,
0.1,
15,
true,
);
scales[j] = sc;
mins[j] = mn;
if sc > max_scale {
max_scale = sc;
}
if mn > max_min {
max_min = mn;
}
}
let mut scales_packed = [0u8; QK_K / 16];
let d_f16: f16;
if max_scale > 0.0 {
let iscale = q4scale / max_scale;
for j in 0..QK_K / 16 {
let l = nearest_int(iscale * scales[j]);
scales_packed[j] = l as u8;
}
d_f16 = f16::from_f32(max_scale / q4scale);
} else {
for j in 0..QK_K / 16 {
scales_packed[j] = 0;
}
d_f16 = f16::from_f32(0.0);
}
let dmin_f16: f16;
if max_min > 0.0 {
let iscale = q4scale / max_min;
for j in 0..QK_K / 16 {
let l = nearest_int(iscale * mins[j]);
scales_packed[j] |= (l as u8) << 4;
}
dmin_f16 = f16::from_f32(max_min / q4scale);
} else {
dmin_f16 = f16::from_f32(0.0);
}
let d_round = d_f16.to_f32();
let dmin_round = dmin_f16.to_f32();
for j in 0..QK_K / 16 {
let d = d_round * (scales_packed[j] & 0xF) as f32;
if d == 0.0 {
continue;
}
let dm = dmin_round * (scales_packed[j] >> 4) as f32;
for ii in 0..16 {
let l = nearest_int((xb[16 * j + ii] + dm) / d);
let l = l.max(0).min(3) as u8;
l_buf[16 * j + ii] = l;
}
}
out.extend_from_slice(&scales_packed);
let mut qs = [0u8; QK_K / 4];
let mut j = 0usize;
while j < QK_K {
for l in 0..32 {
qs[j / 4 + l] = l_buf[j + l]
| (l_buf[j + l + 32] << 2)
| (l_buf[j + l + 64] << 4)
| (l_buf[j + l + 96] << 6);
}
j += 128;
}
out.extend_from_slice(&qs);
out.extend_from_slice(&d_f16.to_le_bytes());
out.extend_from_slice(&dmin_f16.to_le_bytes());
}
}
fn quantize_row_impl(x: &[f32], quant_weights: &[f32], out: &mut Vec<u8>) {
debug_assert_eq!(x.len() % QK_K, 0);
debug_assert_eq!(quant_weights.len(), x.len());
let nb = x.len() / QK_K;
let mut l_buf = [0u8; QK_K];
let mut l_aux = [0u8; 16];
let mut mins = [0.0f32; QK_K / 16];
let mut scales = [0.0f32; QK_K / 16];
let mut sw = [0.0f32; QK_K / 16];
let mut weight = [0.0f32; 16];
let mut ls = [0u8; QK_K / 16];
let mut lm = [0u8; QK_K / 16];
for i in 0..nb {
let xb = &x[QK_K * i..QK_K * (i + 1)];
let qw_row = &quant_weights[QK_K * i..QK_K * (i + 1)];
for v in sw.iter_mut() {
*v = 0.0;
}
let mut sumx2 = 0.0f32;
for j in 0..QK_K {
sumx2 += xb[j] * xb[j];
}
let sigma2 = sumx2 / QK_K as f32;
for j in 0..QK_K / 16 {
let qw = &qw_row[16 * j..16 * j + 16];
for l in 0..16 {
weight[l] = qw[l] * (sigma2 + xb[16 * j + l] * xb[16 * j + l]).sqrt();
}
for l in 0..QK_K / 16 {
sw[j] += weight[l];
}
let mut min_neg = 0.0f32;
let sc = make_qkx3_quants(
16,
3,
&xb[16 * j..16 * j + 16],
Some(&weight),
&mut l_buf[16 * j..16 * j + 16],
&mut min_neg,
&mut l_aux,
-0.9,
0.05,
36,
false,
);
scales[j] = sc;
mins[j] = min_neg; }
let mut dm = make_qp_quants(QK_K / 16, 15, &scales, &mut ls, &sw);
let mut mm = make_qp_quants(QK_K / 16, 15, &mins, &mut lm, &sw);
let d_f16 = f16::from_f32(dm);
let dmin_f16 = f16::from_f32(mm);
dm = d_f16.to_f32();
mm = dmin_f16.to_f32();
let mut scales_packed = [0u8; QK_K / 16];
for j in 0..QK_K / 16 {
scales_packed[j] = ls[j] | (lm[j] << 4);
}
for j in 0..QK_K / 16 {
let d = dm * (scales_packed[j] & 0xF) as f32;
if d == 0.0 {
continue;
}
let m = mm * (scales_packed[j] >> 4) as f32;
for ii in 0..16 {
let l = nearest_int((xb[16 * j + ii] + m) / d);
let l = l.max(0).min(3) as u8;
l_buf[16 * j + ii] = l;
}
}
out.extend_from_slice(&scales_packed);
let mut qs = [0u8; QK_K / 4];
let mut j = 0usize;
while j < QK_K {
for l in 0..32 {
qs[j / 4 + l] = l_buf[j + l]
| (l_buf[j + l + 32] << 2)
| (l_buf[j + l + 64] << 4)
| (l_buf[j + l + 96] << 6);
}
j += 128;
}
out.extend_from_slice(&qs);
out.extend_from_slice(&d_f16.to_le_bytes());
out.extend_from_slice(&dmin_f16.to_le_bytes());
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use std::path::PathBuf;
fn fixture_path(name: &str) -> PathBuf {
let manifest =
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set by cargo test");
PathBuf::from(manifest)
.join("tests/fixtures/ggml_quants")
.join(name)
}
fn read_f32s(name: &str) -> Vec<f32> {
let bytes = fs::read(fixture_path(name)).expect("read fixture");
assert!(
bytes.len() % 4 == 0,
"fixture {} not a multiple of 4 bytes",
name
);
let mut out = Vec::with_capacity(bytes.len() / 4);
for chunk in bytes.chunks_exact(4) {
out.push(f32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]));
}
out
}
fn read_bytes(name: &str) -> Vec<u8> {
fs::read(fixture_path(name)).expect("read fixture")
}
fn make_imatrix(n: usize, seed: u32) -> Vec<f32> {
let mut state = seed;
(0..n)
.map(|_| {
state = state.wrapping_add(0x6D2B79F5);
let mut t = state;
t = (t ^ (t >> 15)).wrapping_mul(t | 1);
t ^= t.wrapping_add((t ^ (t >> 7)).wrapping_mul(t | 61));
let u = t ^ (t >> 14);
let v = (u as f32 / u32::MAX as f32) * 2.0 - 1.0;
v.abs() + 1e-3
})
.collect()
}
#[test]
fn byte_cmp_noim() {
let input = read_f32s("q2_k_512_noim_input.bin");
let expected = read_bytes("q2_k_512_noim_expected.bin");
let got = quantize(&input, 512, None);
assert_eq!(got.len(), expected.len(), "Q2_K noim length mismatch");
assert_eq!(got, expected, "Q2_K noim byte-cmp failed");
}
#[test]
fn byte_cmp_im() {
let input = read_f32s("q2_k_512_im_input.bin");
let expected = read_bytes("q2_k_512_im_expected.bin");
let imatrix = make_imatrix(512, 2);
let got = quantize(&input, 512, Some(&imatrix));
assert_eq!(got.len(), expected.len(), "Q2_K im length mismatch");
assert_eq!(got, expected, "Q2_K im byte-cmp failed");
}
}