#![cfg(feature = "gpu")]
use cortiq_core::format::{CmfHeader, TensorSpec};
use cortiq_core::quant::{f32_to_f16, q2tp_sections, q4tp_put_code, q4tp_sections};
use cortiq_core::types::{ModelArch, QuantType, TensorDtype};
use cortiq_core::CmfModel;
use std::sync::Arc;
const GROUP: usize = 32;
fn encode(vals: &[f32], rows: usize, cols: usize, scale: f32) -> Vec<u8> {
let gpr = cols / GROUP;
let (params_off, codes_off, stride) = q4tp_sections(rows, cols);
let mut out = vec![0u8; codes_off + rows * stride];
let inv = 1.0 / scale;
for r in 0..rows {
for g in 0..gpr {
let tile = &vals[r * cols + g * GROUP..r * cols + (g + 1) * GROUP];
let dst = &mut out[(r * gpr + g) * 16..(r * gpr + g + 1) * 16];
for k in 0..16 {
let q = |v: f32| ((v * inv).round_ties_even().clamp(-8.0, 7.0) as i8 + 8) as u8;
dst[k] = (q(tile[k * 2]) & 0x0F) | (q(tile[k * 2 + 1]) << 4);
}
q4tp_put_code(&mut out[codes_off + r * stride..codes_off + (r + 1) * stride], g, 0);
}
let lo = f32_to_f16(scale.log2());
out[params_off + r * 4..params_off + r * 4 + 2].copy_from_slice(&lo.to_le_bytes());
out[params_off + r * 4 + 2..params_off + r * 4 + 4].copy_from_slice(&0u16.to_le_bytes());
}
out
}
fn encode2(vals: &[f32], rows: usize, cols: usize, scale: f32) -> Vec<u8> {
let gpr = cols / GROUP;
let (params_off, codes_off, stride) = q2tp_sections(rows, cols);
let mut out = vec![0u8; codes_off + rows * stride];
let inv = 1.0 / scale;
for r in 0..rows {
for g in 0..gpr {
let tile = &vals[r * cols + g * GROUP..r * cols + (g + 1) * GROUP];
let dst = &mut out[(r * gpr + g) * 8..(r * gpr + g + 1) * 8];
for k in 0..8 {
let mut byte = 0u8;
for j in 0..4 {
let q = (tile[k * 4 + j] * inv + 1.5).round_ties_even().clamp(0.0, 3.0) as u8;
byte |= q << (2 * j);
}
dst[k] = byte;
}
q4tp_put_code(&mut out[codes_off + r * stride..codes_off + (r + 1) * stride], g, 1);
}
let lo = f32_to_f16(scale.log2());
out[params_off + r * 4..params_off + r * 4 + 2].copy_from_slice(&lo.to_le_bytes());
out[params_off + r * 4 + 2..params_off + r * 4 + 4].copy_from_slice(&0u16.to_le_bytes());
}
out
}
fn noise(n: usize, seed: u64) -> Vec<f32> {
let mut s = seed | 1;
(0..n)
.map(|_| {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
((s >> 40) as f32 / 8_388_608.0) - 1.0
})
.collect()
}
#[test]
fn the_device_gemm_agrees_with_the_host_at_every_batch() {
for (rows, cols) in [
(21504usize, 5376usize), (5376, 7168), (28672, 5376), (5376, 14336), (4096, 5376), ] {
one_shape(rows, cols, false);
}
}
#[test]
fn the_two_bit_device_gemm_agrees_with_the_host() {
for (rows, cols) in [(28672usize, 5376usize), (4096, 5376)] {
one_shape(rows, cols, true);
}
}
fn one_shape(rows: usize, cols: usize, two_bit: bool) {
let scale = 0.01f32;
let w = noise(rows * cols, 0xfeed_face);
let payload = if two_bit {
encode2(&w, rows, cols, scale)
} else {
encode(&w, rows, cols, scale)
};
let dir = std::env::temp_dir().join(format!(
"cmf-tp-batch-{}-{rows}x{cols}-{}",
std::process::id(),
if two_bit { "q2" } else { "q4" }
));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("t.cmf");
let header = CmfHeader {
format: "cmf".into(),
version: cortiq_core::CMF_VERSION,
arch: serde_json::from_value::<ModelArch>(serde_json::json!({
"arch_name": "test", "hidden_size": cols, "intermediate_size": rows,
"num_layers": 1, "num_attention_heads": 1, "num_kv_heads": 1,
"head_dim": 1, "vocab_size": 1, "layer_types": ["FullAttention"],
"rms_norm_eps": 1e-6, "max_position_embeddings": 1,
"linear_conv_kernel_dim": 0, "linear_num_key_heads": 0,
"linear_num_value_heads": 0,
}))
.unwrap(),
quant_type: QuantType::Q4Block,
provenance: None,
tokenizer_config: None,
section_hashes: None,
skills: Vec::new(),
shard: None,
calibration: None,
};
CmfModel::write(
&path,
&header,
&[TensorSpec {
name: "w".into(),
dtype: if two_bit {
TensorDtype::Q2TiledP
} else {
TensorDtype::Q4TiledP
},
shape: vec![rows, cols],
data: payload,
}],
None,
None,
)
.unwrap();
let model = Arc::new(CmfModel::open(&path).unwrap());
let idx = model.tensors.iter().position(|t| t.name == "w").unwrap();
let mut wq = vec![0f32; rows * cols];
cortiq_core::quant::dequant_tensor(&model.tensors[idx], model.entry_bytes(&model.tensors[idx]), &mut wq)
.unwrap();
let mut worst = 0f32;
for &b in &[375usize, 1879] {
let x = noise(b * cols, 0x1234 + b as u64);
let mut got = vec![0f32; b * rows];
let ok = if two_bit {
cortiq_engine::gpu::q2tp_matmat(&model, idx, &x, b, rows, cols, &mut got)
} else {
cortiq_engine::gpu::q4tp_matmat(&model, idx, &x, b, rows, cols, &mut got)
};
if !ok {
eprintln!("b={b}: device declined — skipping");
continue;
}
let mut want = vec![0f32; b * rows];
cortiq_engine::fcd_ops::gemm_nt(&x, &wq, &mut want, b, cols, rows, None);
let nans = got.iter().filter(|v| !v.is_finite()).count();
let d = got
.iter()
.zip(&want)
.fold(0f32, |a, (&p, &q)| a.max((p - q).abs()));
let mag = want.iter().fold(0f32, |a, &v| a.max(v.abs()));
println!(
"[{rows:5} x {cols:5}] b={b:5} {}: max |Δ| {d:.3e} over |ref| {mag:.3e} \
out {:.0} MB non-finite {nans}",
if two_bit { "q2tp" } else { "q4tp" },
(b * rows * 4) as f64 / 1e6
);
assert_eq!(
nans, 0,
"[{rows}x{cols}] b={b}: the device produced {nans} non-finite values"
);
worst = worst.max(d / mag);
}
std::fs::remove_dir_all(&dir).ok();
assert!(
worst < 1e-3,
"[{rows}x{cols}] device disagrees with the host: {worst:.3e} relative"
);
}