const ALIGNMENT: usize = 32;
const GGML_TYPE_F32: u32 = 0;
const GGML_TYPE_Q8_0: u32 = 8;
pub(crate) const GGML_TYPE_Q4_0: u32 = 2;
pub(crate) const GGML_TYPE_Q4_K: u32 = 12;
const GGUF_TYPE_STRING: u32 = 8;
const GGUF_TYPE_U32: u32 = 4;
const GGUF_TYPE_F32: u32 = 6;
#[derive(Clone)]
pub(crate) struct SyntheticModelSpec {
pub architecture: &'static str,
pub n_layers: usize,
pub n_heads: usize,
pub n_kv_heads: usize,
pub hidden_size: usize,
pub ffn_hidden_size: usize,
pub vocab_size: usize,
pub context_length: usize,
pub rope_theta: f32,
pub norm_eps: f32,
pub tie_embeddings: bool,
pub with_qkv_bias: bool,
pub quantize_matmul_weights: bool,
}
impl Default for SyntheticModelSpec {
fn default() -> Self {
Self {
architecture: "qwen2",
n_layers: 2,
n_heads: 4,
n_kv_heads: 2,
hidden_size: 16,
ffn_hidden_size: 32,
vocab_size: 37,
context_length: 64,
rope_theta: 10_000.0,
norm_eps: 1e-6,
tie_embeddings: true,
with_qkv_bias: true,
quantize_matmul_weights: false,
}
}
}
impl SyntheticModelSpec {
pub(crate) fn head_dim(&self) -> usize {
self.hidden_size / self.n_heads
}
pub(crate) fn quantized_benchmark() -> Self {
Self {
hidden_size: 256,
n_heads: 8,
n_kv_heads: 4,
ffn_hidden_size: 512,
vocab_size: 512,
n_layers: 4,
context_length: 512,
..Self::default()
}
}
pub(crate) fn llama_tied_no_bias() -> Self {
Self {
architecture: "llama",
with_qkv_bias: false,
tie_embeddings: true,
rope_theta: 100_000.0,
norm_eps: 1e-5,
..Self::default()
}
}
}
struct Xorshift64(u64);
impl Xorshift64 {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_weight(&mut self) -> f32 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
let unit = (self.0 >> 40) as u32 as f32 / (1u32 << 24) as f32; (unit - 0.5) * 0.2
}
fn next_byte(&mut self) -> u8 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
(self.0 >> 33) as u8
}
}
fn push_u32(buf: &mut Vec<u8>, v: u32) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_u64(buf: &mut Vec<u8>, v: u64) {
buf.extend_from_slice(&v.to_le_bytes());
}
fn push_string(buf: &mut Vec<u8>, s: &str) {
push_u64(buf, s.len() as u64);
buf.extend_from_slice(s.as_bytes());
}
fn pad_to_alignment(buf: &mut Vec<u8>, alignment: usize) {
let pad = alignment.wrapping_sub(buf.len() % alignment) % alignment;
buf.extend(std::iter::repeat_n(0u8, pad));
}
fn quantize_q8_0_blocks(data: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len() / 32 * 34);
for chunk in data.chunks_exact(32) {
let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
let d = amax / 127.0;
let id = if d != 0.0 { 1.0 / d } else { 0.0 };
out.extend_from_slice(&kopitiam_tensor::f32_to_f16(d).to_le_bytes());
for &v in chunk {
let q = (v * id).round().clamp(-127.0, 127.0) as i8;
out.push(q as u8);
}
}
out
}
pub(crate) fn quantize_q4_0_blocks(data: &[f32]) -> Vec<u8> {
let mut out = Vec::with_capacity(data.len() / 32 * 18);
for chunk in data.chunks_exact(32) {
let amax = chunk.iter().fold(0f32, |m, &v| m.max(v.abs()));
let d = amax / 7.0;
let id = if d != 0.0 { 1.0 / d } else { 0.0 };
out.extend_from_slice(&kopitiam_tensor::f32_to_f16(d).to_le_bytes());
let nibble = |v: f32| -> u8 { ((v * id).round().clamp(-8.0, 7.0) as i32 + 8) as u8 & 0x0F };
for j in 0..16 {
out.push(nibble(chunk[j]) | (nibble(chunk[j + 16]) << 4));
}
}
out
}
pub(crate) fn arbitrary_q4_k_blocks(n_superblocks: usize, seed: u64) -> Vec<u8> {
let mut rng = Xorshift64::new(seed);
let mut out = Vec::with_capacity(n_superblocks * 144);
for _ in 0..n_superblocks {
out.extend_from_slice(&kopitiam_tensor::f32_to_f16(0.05).to_le_bytes()); out.extend_from_slice(&kopitiam_tensor::f32_to_f16(0.02).to_le_bytes()); for _ in 0..(12 + 128) {
out.push(rng.next_byte());
}
}
out
}
struct GgufBuilder {
kv_count: u64,
kvs: Vec<u8>,
tensor_count: u64,
tensor_infos: Vec<u8>,
tensor_data: Vec<u8>,
}
impl GgufBuilder {
fn new() -> Self {
Self { kv_count: 0, kvs: Vec::new(), tensor_count: 0, tensor_infos: Vec::new(), tensor_data: Vec::new() }
}
fn kv_string(&mut self, key: &str, value: &str) {
push_string(&mut self.kvs, key);
push_u32(&mut self.kvs, GGUF_TYPE_STRING);
push_string(&mut self.kvs, value);
self.kv_count += 1;
}
fn kv_u32(&mut self, key: &str, value: u32) {
push_string(&mut self.kvs, key);
push_u32(&mut self.kvs, GGUF_TYPE_U32);
push_u32(&mut self.kvs, value);
self.kv_count += 1;
}
fn kv_f32(&mut self, key: &str, value: f32) {
push_string(&mut self.kvs, key);
push_u32(&mut self.kvs, GGUF_TYPE_F32);
self.kvs.extend_from_slice(&value.to_le_bytes());
self.kv_count += 1;
}
fn tensor_f32(&mut self, name: &str, shape: &[usize], data: &[f32]) {
assert_eq!(shape.iter().product::<usize>(), data.len(), "tensor {name}: shape/data length mismatch");
pad_to_alignment(&mut self.tensor_data, ALIGNMENT);
let relative_offset = self.tensor_data.len() as u64;
let ne: Vec<u64> = shape.iter().rev().map(|&d| d as u64).collect();
push_string(&mut self.tensor_infos, name);
push_u32(&mut self.tensor_infos, ne.len() as u32);
for &d in &ne {
push_u64(&mut self.tensor_infos, d);
}
push_u32(&mut self.tensor_infos, GGML_TYPE_F32);
push_u64(&mut self.tensor_infos, relative_offset);
self.tensor_count += 1;
for &v in data {
self.tensor_data.extend_from_slice(&v.to_le_bytes());
}
}
fn tensor_q8_0(&mut self, name: &str, shape: &[usize], data: &[f32]) {
let elems: usize = shape.iter().product();
assert_eq!(elems, data.len(), "tensor {name}: shape/data length mismatch");
let in_features = *shape.last().expect("Q8_0 weight tensors are at least rank 1");
assert!(
in_features.is_multiple_of(32),
"tensor {name}: Q8_0 requires each row ({in_features} elements) to be a whole number of 32-element blocks"
);
pad_to_alignment(&mut self.tensor_data, ALIGNMENT);
let relative_offset = self.tensor_data.len() as u64;
let ne: Vec<u64> = shape.iter().rev().map(|&d| d as u64).collect();
push_string(&mut self.tensor_infos, name);
push_u32(&mut self.tensor_infos, ne.len() as u32);
for &d in &ne {
push_u64(&mut self.tensor_infos, d);
}
push_u32(&mut self.tensor_infos, GGML_TYPE_Q8_0);
push_u64(&mut self.tensor_infos, relative_offset);
self.tensor_count += 1;
self.tensor_data.extend(quantize_q8_0_blocks(data));
}
fn tensor_raw_quantized(&mut self, name: &str, shape: &[usize], ggml_type: u32, bytes: &[u8]) {
pad_to_alignment(&mut self.tensor_data, ALIGNMENT);
let relative_offset = self.tensor_data.len() as u64;
let ne: Vec<u64> = shape.iter().rev().map(|&d| d as u64).collect();
push_string(&mut self.tensor_infos, name);
push_u32(&mut self.tensor_infos, ne.len() as u32);
for &d in &ne {
push_u64(&mut self.tensor_infos, d);
}
push_u32(&mut self.tensor_infos, ggml_type);
push_u64(&mut self.tensor_infos, relative_offset);
self.tensor_count += 1;
self.tensor_data.extend_from_slice(bytes);
}
fn matmul_weight(&mut self, quantize: bool, name: &str, shape: &[usize], data: &[f32]) {
if quantize {
self.tensor_q8_0(name, shape, data);
} else {
self.tensor_f32(name, shape, data);
}
}
fn build(self) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(b"GGUF");
push_u32(&mut buf, 3); push_u64(&mut buf, self.tensor_count);
push_u64(&mut buf, self.kv_count);
buf.extend_from_slice(&self.kvs);
buf.extend_from_slice(&self.tensor_infos);
pad_to_alignment(&mut buf, ALIGNMENT);
buf.extend_from_slice(&self.tensor_data);
buf
}
}
pub(crate) fn build(spec: &SyntheticModelSpec) -> Vec<u8> {
let mut rng = Xorshift64::new(0xC0FFEE_u64);
let fill = |n: usize, rng: &mut Xorshift64| -> Vec<f32> { (0..n).map(|_| rng.next_weight()).collect() };
let mut g = GgufBuilder::new();
let arch = spec.architecture;
g.kv_string("general.architecture", arch);
g.kv_u32(&format!("{arch}.block_count"), spec.n_layers as u32);
g.kv_u32(&format!("{arch}.attention.head_count"), spec.n_heads as u32);
g.kv_u32(&format!("{arch}.attention.head_count_kv"), spec.n_kv_heads as u32);
g.kv_u32(&format!("{arch}.embedding_length"), spec.hidden_size as u32);
g.kv_u32(&format!("{arch}.feed_forward_length"), spec.ffn_hidden_size as u32);
g.kv_u32(&format!("{arch}.context_length"), spec.context_length as u32);
g.kv_u32(&format!("{arch}.vocab_size"), spec.vocab_size as u32);
g.kv_f32(&format!("{arch}.rope.freq_base"), spec.rope_theta);
g.kv_f32(&format!("{arch}.attention.layer_norm_rms_epsilon"), spec.norm_eps);
let hidden = spec.hidden_size;
let kv_dim = spec.n_kv_heads * spec.head_dim();
let ffn = spec.ffn_hidden_size;
let vocab = spec.vocab_size;
g.tensor_f32("token_embd.weight", &[vocab, hidden], &fill(vocab * hidden, &mut rng));
for layer in 0..spec.n_layers {
let p = |suffix: &str| format!("blk.{layer}.{suffix}");
let q = spec.quantize_matmul_weights;
g.tensor_f32(&p("attn_norm.weight"), &[hidden], &fill(hidden, &mut rng));
g.matmul_weight(q, &p("attn_q.weight"), &[hidden, hidden], &fill(hidden * hidden, &mut rng));
g.matmul_weight(q, &p("attn_k.weight"), &[kv_dim, hidden], &fill(kv_dim * hidden, &mut rng));
g.matmul_weight(q, &p("attn_v.weight"), &[kv_dim, hidden], &fill(kv_dim * hidden, &mut rng));
if spec.with_qkv_bias {
g.tensor_f32(&p("attn_q.bias"), &[hidden], &fill(hidden, &mut rng));
g.tensor_f32(&p("attn_k.bias"), &[kv_dim], &fill(kv_dim, &mut rng));
g.tensor_f32(&p("attn_v.bias"), &[kv_dim], &fill(kv_dim, &mut rng));
}
g.matmul_weight(q, &p("attn_output.weight"), &[hidden, hidden], &fill(hidden * hidden, &mut rng));
g.tensor_f32(&p("ffn_norm.weight"), &[hidden], &fill(hidden, &mut rng));
g.matmul_weight(q, &p("ffn_gate.weight"), &[ffn, hidden], &fill(ffn * hidden, &mut rng));
g.matmul_weight(q, &p("ffn_up.weight"), &[ffn, hidden], &fill(ffn * hidden, &mut rng));
g.matmul_weight(q, &p("ffn_down.weight"), &[hidden, ffn], &fill(hidden * ffn, &mut rng));
}
g.tensor_f32("output_norm.weight", &[hidden], &fill(hidden, &mut rng));
if !spec.tie_embeddings {
g.matmul_weight(spec.quantize_matmul_weights, "output.weight", &[vocab, hidden], &fill(vocab * hidden, &mut rng));
}
g.build()
}
pub(crate) fn tiny_model_bytes() -> Vec<u8> {
build(&SyntheticModelSpec::default())
}
pub(crate) fn single_quantized_weight_gguf(name: &str, shape: &[usize], ggml_type: u32, bytes: &[u8]) -> Vec<u8> {
let mut g = GgufBuilder::new();
g.tensor_raw_quantized(name, shape, ggml_type, bytes);
g.build()
}
pub(crate) fn write_temp_gguf(bytes: &[u8], disambiguator: &str) -> std::path::PathBuf {
use std::io::Write;
let mut tmp = tempfile::Builder::new()
.prefix(&format!("kopitiam-runtime-test-{disambiguator}-"))
.suffix(".gguf")
.tempfile()
.expect("create a uniquely-named temp file");
tmp.write_all(bytes).expect("write synthetic GGUF fixture");
tmp.flush().expect("flush synthetic GGUF fixture");
let (_file, path) = tmp.keep().expect("persist temp GGUF fixture past this function's return");
path
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tiny_model_bytes_parses_as_valid_gguf_with_expected_shapes() {
let bytes = tiny_model_bytes();
let path = write_temp_gguf(&bytes, "self-check");
let model = kopitiam_loader::load_model(&path).unwrap();
assert_eq!(model.format(), "gguf");
let embd = model.tensor("token_embd.weight").unwrap();
assert_eq!(embd.shape.dims(), &[37, 16]);
assert!(model.tensor("blk.0.attn_q.weight").is_some());
assert!(model.tensor("blk.1.ffn_down.weight").is_some());
assert!(model.tensor("output.weight").is_none());
}
#[test]
fn quantized_spec_writes_real_q8_0_tensors_the_loader_recognizes() {
let spec = SyntheticModelSpec { quantize_matmul_weights: true, ..SyntheticModelSpec::quantized_benchmark() };
let bytes = build(&spec);
let path = write_temp_gguf(&bytes, "quantized-self-check");
let model = kopitiam_loader::load_model(&path).unwrap();
let wq = model.tensor("blk.0.attn_q.weight").unwrap();
assert_eq!(wq.dtype, kopitiam_core::DType::Q8_0);
assert_eq!(wq.shape.dims(), &[spec.hidden_size, spec.hidden_size]);
let embd = model.tensor("token_embd.weight").unwrap();
assert_eq!(embd.dtype, kopitiam_core::DType::F32, "embeddings must never be quantized by this fixture");
}
#[test]
fn quantize_q8_0_blocks_round_trips_within_one_quantization_step() {
let data: Vec<f32> = (0..32).map(|j| (j as f32 - 16.0) * 0.3).collect();
let bytes = quantize_q8_0_blocks(&data);
assert_eq!(bytes.len(), 34);
let d = kopitiam_tensor::f16_to_f32(u16::from_le_bytes([bytes[0], bytes[1]]));
for (j, &orig) in data.iter().enumerate() {
let decoded = f32::from(bytes[2 + j] as i8) * d;
assert!((decoded - orig).abs() <= d / 2.0 + 1e-6, "index {j}: {decoded} vs {orig} (d={d})");
}
}
}