use std::io::Write;
use std::path::Path;
use std::process::Command;
const GATE_PROJ_GOLDEN: &[u8] =
include_bytes!("fixtures/quantize_q4_reader_refactor/gate_proj_golden.q4");
const K_PROJ_GOLDEN: &[u8] =
include_bytes!("fixtures/quantize_q4_reader_refactor/k_proj_golden.q4");
const CONFIG_JSON: &str = "{\n \"model_type\": \"fixture\"\n}\n";
fn f32_to_bf16_bits(v: f32) -> u16 {
(v.to_bits() >> 16) as u16
}
fn gen_values(n: usize, seed: usize) -> Vec<f32> {
(0..n)
.map(|i| (((i * 37 + seed * 13) % 200) as i64 - 100) as f32 / 4.0)
.collect()
}
fn write_safetensors(path: &Path, tensors: &[(&str, Vec<usize>, Vec<f32>)]) {
let mut header = serde_json::Map::new();
let mut payload: Vec<u8> = Vec::new();
for (name, shape, values) in tensors {
let start = payload.len();
for &v in values {
payload.extend_from_slice(&f32_to_bf16_bits(v).to_le_bytes());
}
let end = payload.len();
let mut entry = serde_json::Map::new();
entry.insert("dtype".into(), serde_json::Value::String("BF16".into()));
entry.insert(
"shape".into(),
serde_json::Value::Array(shape.iter().map(|d| serde_json::Value::from(*d)).collect()),
);
entry.insert(
"data_offsets".into(),
serde_json::Value::Array(vec![
serde_json::Value::from(start),
serde_json::Value::from(end),
]),
);
header.insert((*name).to_string(), serde_json::Value::Object(entry));
}
let header_bytes = serde_json::to_string(&serde_json::Value::Object(header))
.expect("header serializes")
.into_bytes();
let mut file = std::fs::File::create(path).expect("create safetensors file");
file.write_all(&(header_bytes.len() as u64).to_le_bytes())
.expect("write header length");
file.write_all(&header_bytes).expect("write header");
file.write_all(&payload).expect("write payload");
}
fn write_safetensors_f16(path: &Path, tensors: &[(&str, Vec<usize>, Vec<u16>)]) {
let mut header = serde_json::Map::new();
let mut payload: Vec<u8> = Vec::new();
for (name, shape, values) in tensors {
let start = payload.len();
for &v in values {
payload.extend_from_slice(&v.to_le_bytes());
}
let end = payload.len();
let mut entry = serde_json::Map::new();
entry.insert("dtype".into(), serde_json::Value::String("F16".into()));
entry.insert(
"shape".into(),
serde_json::Value::Array(shape.iter().map(|d| serde_json::Value::from(*d)).collect()),
);
entry.insert(
"data_offsets".into(),
serde_json::Value::Array(vec![
serde_json::Value::from(start),
serde_json::Value::from(end),
]),
);
header.insert((*name).to_string(), serde_json::Value::Object(entry));
}
let header_bytes = serde_json::to_string(&serde_json::Value::Object(header))
.expect("header serializes")
.into_bytes();
let mut file = std::fs::File::create(path).expect("create safetensors file");
file.write_all(&(header_bytes.len() as u64).to_le_bytes())
.expect("write header length");
file.write_all(&header_bytes).expect("write header");
file.write_all(&payload).expect("write payload");
}
fn fixture_tensors() -> Vec<(&'static str, Vec<usize>, Vec<f32>)> {
vec![
(
"model.layers.0.mlp.gate_proj.weight",
vec![2, 33],
gen_values(66, 1),
),
(
"model.layers.0.self_attn.k_proj.weight",
vec![1, 32],
gen_values(32, 2),
),
(
"model.layers.0.input_layernorm.weight",
vec![4],
gen_values(4, 3),
),
]
}
fn write_sharded_fixture(model_dir: &Path) {
std::fs::create_dir_all(model_dir).expect("create model dir");
std::fs::write(model_dir.join("config.json"), CONFIG_JSON).expect("write config");
let shard_name = "model-00001-of-00001.safetensors";
write_safetensors(&model_dir.join(shard_name), &fixture_tensors());
let weight_map: serde_json::Map<String, serde_json::Value> = fixture_tensors()
.into_iter()
.map(|(name, _, _)| {
(
name.to_string(),
serde_json::Value::String(shard_name.into()),
)
})
.collect();
let index = serde_json::json!({
"metadata": {},
"weight_map": serde_json::Value::Object(weight_map),
});
std::fs::write(
model_dir.join("model.safetensors.index.json"),
serde_json::to_string(&index).expect("index serializes"),
)
.expect("write index");
}
fn write_single_file_fixture(model_dir: &Path) {
std::fs::create_dir_all(model_dir).expect("create model dir");
std::fs::write(model_dir.join("config.json"), CONFIG_JSON).expect("write config");
write_safetensors(&model_dir.join("model.safetensors"), &fixture_tensors());
}
fn run_quantize_q4(model_dir: &Path, output_dir: &Path) {
let bin = env!("CARGO_BIN_EXE_quantize_q4");
let status = Command::new(bin)
.arg("--model-dir")
.arg(model_dir)
.arg("--output-dir")
.arg(output_dir)
.status()
.expect("spawn quantize_q4");
assert!(status.success(), "quantize_q4 exited with {status}");
}
fn assert_q4_matches_golden(output_dir: &Path) {
let gate_proj =
std::fs::read(output_dir.join("model_layers_0_mlp_gate_proj_weight.q4")).unwrap();
let k_proj =
std::fs::read(output_dir.join("model_layers_0_self_attn_k_proj_weight.q4")).unwrap();
assert_eq!(
gate_proj, GATE_PROJ_GOLDEN,
"gate_proj .q4 bytes diverged from the pre-refactor golden"
);
assert_eq!(
k_proj, K_PROJ_GOLDEN,
"k_proj .q4 bytes diverged from the pre-refactor golden"
);
}
#[test]
fn sharded_fixture_matches_pre_refactor_q4_golden() {
let dir = tempfile::tempdir().expect("tempdir");
let model_dir = dir.path().join("model");
let output_dir = dir.path().join("out");
write_sharded_fixture(&model_dir);
run_quantize_q4(&model_dir, &output_dir);
assert_q4_matches_golden(&output_dir);
let index: serde_json::Value = serde_json::from_str(
&std::fs::read_to_string(output_dir.join("quantize_index.json")).unwrap(),
)
.expect("index parses");
let entries = index.as_array().expect("index is an array");
assert_eq!(entries.len(), 3);
let names: Vec<&str> = entries
.iter()
.map(|e| e["name"].as_str().unwrap())
.collect();
assert!(names.contains(&"model.layers.0.mlp.gate_proj.weight"));
assert!(names.contains(&"model.layers.0.self_attn.k_proj.weight"));
assert!(names.contains(&"model.layers.0.input_layernorm.weight"));
let quantized_flags: Vec<bool> = entries
.iter()
.map(|e| e["quantized"].as_bool().unwrap())
.collect();
assert_eq!(quantized_flags.iter().filter(|&&q| q).count(), 2);
}
#[test]
fn single_file_fixture_matches_same_q4_golden() {
let dir = tempfile::tempdir().expect("tempdir");
let model_dir = dir.path().join("model");
let output_dir = dir.path().join("out");
write_single_file_fixture(&model_dir);
run_quantize_q4(&model_dir, &output_dir);
assert_q4_matches_golden(&output_dir);
}
#[test]
fn output_includes_source_config_json_verbatim() {
let dir = tempfile::tempdir().expect("tempdir");
let model_dir = dir.path().join("model");
let output_dir = dir.path().join("out");
write_single_file_fixture(&model_dir);
run_quantize_q4(&model_dir, &output_dir);
let output_config = std::fs::read_to_string(output_dir.join("config.json"))
.expect("quantized artifact includes config.json");
assert_eq!(output_config, CONFIG_JSON);
}
fn read_kept_f16_output(path: &Path) -> Vec<u16> {
let bytes = std::fs::read(path).expect("read kept f16 output");
assert_eq!(&bytes[0..4], b"KHF1", "unexpected magic");
let ndim = u32::from_le_bytes(bytes[8..12].try_into().unwrap()) as usize;
let shape_end = 12 + ndim * 8;
let numel = u64::from_le_bytes(bytes[shape_end..shape_end + 8].try_into().unwrap()) as usize;
let data_start = shape_end + 8;
bytes[data_start..data_start + numel * 2]
.chunks_exact(2)
.map(|c| u16::from_le_bytes([c[0], c[1]]))
.collect()
}
#[test]
fn f16_source_subnormals_survive_kept_tensor_round_trip() {
let dir = tempfile::tempdir().expect("tempdir");
let model_dir = dir.path().join("model");
let output_dir = dir.path().join("out");
std::fs::create_dir_all(&model_dir).expect("create model dir");
std::fs::write(model_dir.join("config.json"), CONFIG_JSON).expect("write config");
let values: Vec<u16> = vec![
0x0001, 0x03ff, 0x8001, 0x83ff, ];
write_safetensors_f16(
&model_dir.join("model.safetensors"),
&[(
"model.layers.0.input_layernorm.weight",
vec![values.len()],
values.clone(),
)],
);
run_quantize_q4(&model_dir, &output_dir);
let out_path = output_dir.join("model_layers_0_input_layernorm_weight.f16");
let round_tripped = read_kept_f16_output(&out_path);
assert_eq!(
round_tripped, values,
"F16 subnormal bit patterns did not survive the kept-tensor round trip"
);
}