#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::indexing_slicing,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::as_conversions,
clippy::missing_docs_in_private_items,
clippy::missing_panics_doc,
missing_docs
)]
use std::path::PathBuf;
use candle_core::{DType, Device, IndexOp, Tensor};
use candle_mi::{HookSpec, MIBackend, MIModel};
const BAR_BNB: f32 = 1.5;
const BAR_AWQ: f32 = 1e-1;
const BAR_GPTQ: f32 = 1e-1;
fn reference_path(filename: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("scripts")
.join(filename)
}
fn model_is_cached(model_id: &str) -> bool {
let Some(home) = std::env::var_os("USERPROFILE").or_else(|| std::env::var_os("HOME")) else {
return false;
};
let dir = std::path::Path::new(&home)
.join(".cache")
.join("huggingface")
.join("hub")
.join(format!("models--{}", model_id.replace('/', "--")));
dir.exists()
}
fn run_quantized_parity(scheme: &str, model_id: &str, oracle_file: &str, bar: f32) {
if !model_is_cached(model_id) {
eprintln!("SKIP: {model_id} not in HF cache");
return;
}
let reference_str = std::fs::read_to_string(reference_path(oracle_file)).unwrap_or_else(|_| {
panic!("read {oracle_file} — run the matching scripts/*_validation.py")
});
let reference: serde_json::Value = serde_json::from_str(&reference_str).unwrap();
assert_eq!(reference["model_repo"].as_str().unwrap(), model_id);
let ref_vocab = reference["vocab_size"].as_u64().unwrap() as usize;
let test_cases = reference["test_cases"].as_array().unwrap();
let model = MIModel::from_pretrained(model_id)
.unwrap_or_else(|e| panic!("load {scheme} model {model_id} via anamnesis dequant: {e}"));
println!("Validating {scheme} forward parity (anamnesis dequant) vs real-library oracle:");
println!(
" model: {model_id} (num_layers={}, hidden_size={}, vocab_size={})",
model.num_layers(),
model.hidden_size(),
model.vocab_size()
);
assert_eq!(model.vocab_size(), ref_vocab);
let device = model.device().clone();
let mut max_abs_diff: f32 = 0.0;
let mut failures: Vec<String> = Vec::new();
for tc in test_cases {
let prompt = tc["prompt"].as_str().unwrap();
let ref_tokens: Vec<u32> = tc["tokens"]
.as_array()
.unwrap()
.iter()
.map(|v| v.as_u64().unwrap() as u32)
.collect();
let ref_top10 = tc["top_10"].as_array().unwrap();
let input = Tensor::new(&ref_tokens[..], &device)
.unwrap()
.unsqueeze(0)
.unwrap();
let hooks = HookSpec::new();
let cache = model.forward(&input, &hooks).unwrap();
let logits = cache.output();
let (batch, out_seq, vocab) = logits.dims3().unwrap();
assert_eq!(batch, 1);
assert_eq!(out_seq, ref_tokens.len());
assert_eq!(vocab, ref_vocab);
let last: Vec<f32> = logits
.i((0, out_seq - 1))
.unwrap()
.to_device(&Device::Cpu)
.unwrap()
.to_dtype(DType::F32)
.unwrap()
.to_vec1()
.unwrap();
let mut indexed: Vec<(usize, f32)> =
last.iter().enumerate().map(|(i, &v)| (i, v)).collect();
indexed.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
let ref_top1_idx = ref_top10[0]["index"].as_u64().unwrap() as usize;
let ref_top1_logit = ref_top10[0]["logit"].as_f64().unwrap() as f32;
println!(
" {prompt:?}: oracle top-1 ({ref_top1_idx}, {ref_top1_logit:.4}) candle ({}, {:.4})",
indexed[0].0, indexed[0].1
);
if indexed[0].0 != ref_top1_idx {
failures.push(format!(
"{prompt:?}: top-1 mismatch (candle {}, oracle {ref_top1_idx})",
indexed[0].0
));
}
for ref_item in ref_top10 {
let ref_idx = ref_item["index"].as_u64().unwrap() as usize;
let ref_logit = ref_item["logit"].as_f64().unwrap() as f32;
let diff = (last[ref_idx] - ref_logit).abs();
if diff >= bar {
failures.push(format!(
"{prompt:?} token {ref_idx}: logit abs-diff {diff:.3e} >= {bar:.2} \
(candle {:.4}, oracle {ref_logit:.4})",
last[ref_idx]
));
}
max_abs_diff = max_abs_diff.max(diff);
}
}
println!(
"{} test cases; max abs-diff across all top-10 tokens = {:.3e} (bar: {:.2})",
test_cases.len(),
max_abs_diff,
bar
);
assert!(
failures.is_empty(),
"{scheme} forward parity FAILED ({} divergences):\n {}",
failures.len(),
failures.join("\n ")
);
}
#[test]
#[ignore = "requires medmekk/Llama-3.2-1B-Instruct-bnb-nf4 cached, a CUDA device, and the `quantized` feature; run with --ignored"]
fn bnb_nf4_llama_forward_parity() {
run_quantized_parity(
"bnb-NF4",
"medmekk/Llama-3.2-1B-Instruct-bnb-nf4",
"bnb_nf4_forward_reference.json",
BAR_BNB,
);
}
#[test]
#[ignore = "requires casperhansen/llama-3.2-1b-instruct-awq cached, a CUDA device, and the `quantized` feature; run with --ignored"]
fn awq_llama_forward_parity() {
run_quantized_parity(
"AWQ",
"casperhansen/llama-3.2-1b-instruct-awq",
"awq_forward_reference.json",
BAR_AWQ,
);
}
#[test]
#[ignore = "requires shuyuej/Llama-3.2-1B-Instruct-GPTQ cached, a CUDA device, and the `quantized` feature; run with --ignored"]
fn gptq_llama_forward_parity() {
run_quantized_parity(
"GPTQ",
"shuyuej/Llama-3.2-1B-Instruct-GPTQ",
"gptq_forward_reference.json",
BAR_GPTQ,
);
}