Five crates ported / designed. All 235 tests pass; 0 errors;
0 warnings outside the loader-gguf NEON-intrinsics module (suppressed
with a documented #![allow(unsafe_op_in_unsafe_fn)] — proper fix is
wrapping each intrinsic call in unsafe { }, tracked as kernel-quality
maintenance, not standards work).
jouleclaw-cascade
Ported from pattern-lang/joule-cascade (~7044 LOC, 17 files).
L0 (Cache), tier dispatch, 7-axis Synthesis coordinate router, cost
model, calibration ledger (in-memory + disk), history, verification,
body + body_safety policy gates, active-entity tracking, l2_service
trait surface. Two tests that depended on joule-l1's lawful-primitive
impl (which requires pattern-lang's pattern-core + cortex-ir +
synthesizer — OpenIE IP) were removed; the L1 trait surface lives
here and consumers plug their own lawful lexicon in.
jouleclaw-loader-gguf
Ported from pattern-lang/joule-loader-gguf (~11605 LOC, 17 files).
GGUF v3 parser with mmap, Llama and Gemma / Gemma 4 decoders,
Q4_0 / Q5_K / Q8_0 / Q8_0 grouped quants, in-place KV cache,
matmul_q8_0 with NEON intrinsics on Apple Silicon, safetensors
reader, tokenizer + Gemma tokenizer, sample, dequant.
jouleclaw-pack (NEW)
The .jc.toml sidecar — JouleClaw's standard-defining contribution.
A model on disk (GGUF / safetensors / MLX) tells you tensor shapes
and quant schemes. It does NOT tell you what one forward pass
actually costs in joules on real hardware. The .jc.toml is a
declared-cost contract that travels with the model:
- per-tensor precision (FP16/BF16/FP8/INT8/NVFP4/NF4/Q4_K/...)
- page-alignment status (mmap-zero-copy possible? on which UMA?)
- measured J/op on a reference hardware matrix with explicit
Provenance (HwShunt / ModelBased / Estimator)
- drift_factor tolerance per measurement; the runtime trips
PackError::CostDrift when measured > declared * drift_factor
and down-weights the backend in subsequent auction rounds
Without a declared-cost contract, "energy-optimized inference" is
marketing. With one, it is an engineering claim any third party
can verify against the published reference-hardware corpus.
5 lib tests pass. ModelFormat, Precision, Alignment, MeasurementOp,
Pack, TensorContract, Measurement, hash_model(), verify_measurement().
jouleclaw-history
Ported from pattern-lang/joule-history (~2026 LOC, 6 files).
Persistent resolution history: disk + in-memory + tiered
(hot/warm/cold) + semantic. One Rust-2024 edition fix in
tiered.rs (binding-mode pattern: |(_, &seq)| seq -> |(_, seq)| *seq).
jouleclaw-wire
Ported from pattern-lang/joule-wire (~1129 LOC, 2 files).
L4 wire tier — the remote frontier escape hatch. RPC client +
transport, invoked only when the local cascade returns Unresolvable.
Phase 3 next: HAL from efficient-genai, backend-{apple,reference,
cuda,metal,rocm,vulkan}, runtime (port joule-runtime — chat/embed/
generate/streaming/PLD/drafter — needs backends to compile).
Co-Authored-By: Claude Opus 4.7 <[email protected]>
161 lines
6.6 KiB
Rust
161 lines
6.6 KiB
Rust
//! End-to-end Gemma 4 E2B oracles. Ignored by default — they need
|
|
//! the real ~10 GB checkpoint at `models/gemma-4-E2B`.
|
|
//!
|
|
//! Every test in this file gates **correctness of the model output**
|
|
//! against the HF transformers float32 reference. There are no
|
|
//! "kernel-runs-without-panic" tests here.
|
|
//!
|
|
//! Run: `cargo test -p jouleclaw-loader-gguf --test real_gemma4_oracle -- --ignored --nocapture`
|
|
|
|
use std::io::Write;
|
|
|
|
const DIR: &str = "/Users/dcharlot/data-share/vibe-coding/pattern-lang/models/gemma-4-E2B";
|
|
const OUT: &str = "/Users/dcharlot/data-share/vibe-coding/pattern-lang/tmp/gemma4-oracle";
|
|
|
|
fn dump(name: &str, v: &[f32]) {
|
|
let mut f = std::fs::File::create(format!("{OUT}/rust_{name}.bin")).unwrap();
|
|
let mut b = Vec::with_capacity(v.len() * 4);
|
|
for x in v {
|
|
b.extend_from_slice(&x.to_le_bytes());
|
|
}
|
|
f.write_all(&b).unwrap();
|
|
}
|
|
|
|
/// Single-pass forward — every staged tensor matches HF and the
|
|
/// post-softcap top-5 ids+values are byte-equal to HF's.
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_predicts_paris() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let m = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load");
|
|
eprintln!(
|
|
"loaded gemma4 E2B: {} layers, d={}, vocab={}",
|
|
m.cfg.n_layers, m.cfg.d_model, m.cfg.vocab
|
|
);
|
|
let ids: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let o = m.forward(&ids);
|
|
|
|
dump("embed", &o.embed);
|
|
dump("layer0", &o.layer0);
|
|
dump("layer14", &o.layer14);
|
|
dump("layer34", &o.layer_last);
|
|
dump("final", &o.final_norm);
|
|
dump("logits_post", &o.logits_post);
|
|
dump("logits_pre", &o.logits_pre);
|
|
dump("l0_attn", &o.l0_attn);
|
|
dump("l0_post_attn_norm", &o.l0_post_attn_norm);
|
|
dump("l0_mlp", &o.l0_mlp);
|
|
dump("l0_post_ffw_norm", &o.l0_post_ffw_norm);
|
|
dump("l0_post_ple_norm", &o.l0_post_ple_norm);
|
|
|
|
let mut idx: Vec<usize> = (0..o.logits_post.len()).collect();
|
|
idx.sort_by(|&a, &b| o.logits_post[b].partial_cmp(&o.logits_post[a]).unwrap());
|
|
let hf_top5 = [9079usize, 496, 506, 3224, 886];
|
|
assert_eq!(&idx[..5], &hf_top5, "top-5 ids diverge from HF reference");
|
|
let v0 = o.logits_post[idx[0]];
|
|
assert!(
|
|
(v0 - 22.5101).abs() < 0.05,
|
|
"top post-softcap logit {v0} != HF 22.5101 (±0.05)"
|
|
);
|
|
}
|
|
|
|
/// Greedy generation (uncached, full re-prefill per step) — 8-token
|
|
/// continuation must equal HF `generate(do_sample=False)` exactly.
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_greedy_matches_hf() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let m = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load");
|
|
let prompt: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let got = m.generate(&prompt, 8);
|
|
let hf: Vec<u32> = vec![9079, 236761, 108, 818, 5279, 529, 7001, 563];
|
|
eprintln!("rust greedy +8: {got:?}");
|
|
eprintln!("HF greedy +8: {hf:?} (' Paris.\\n\\nThe capital of France is')");
|
|
assert_eq!(got, hf, "greedy decode diverges from HF reference");
|
|
}
|
|
|
|
/// KV-cached generate equals the uncached path equals HF reference.
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_kvcache_parity() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let m = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load");
|
|
let prompt: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let hf: Vec<u32> = vec![9079, 236761, 108, 818, 5279, 529, 7001, 563];
|
|
let cached = m.generate_cached(&prompt, 8);
|
|
assert_eq!(cached, hf, "kv-cached decode diverges from HF reference");
|
|
let uncached = m.generate(&prompt, 8);
|
|
assert_eq!(cached, uncached, "kv-cached != uncached generate");
|
|
}
|
|
|
|
/// Per-row int8 (Q8) — token-identical to HF on the short prompt.
|
|
/// (Note: per-row Q8 *drifts* at the 8th token on the longest
|
|
/// benchmark prompt; per-group Q8 below is the production tier.)
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_q8_token_identical_on_paris() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let g = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load f32");
|
|
let q = jouleclaw_loader_gguf::gemma4_q8::Gemma4Q8::from_gemma4(&g);
|
|
let prompt: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let hf: Vec<u32> = vec![9079, 236761, 108, 818, 5279, 529, 7001, 563];
|
|
let got = q.generate_cached(&prompt, 8);
|
|
eprintln!("Q8 greedy +8: {got:?}");
|
|
eprintln!("HF reference: {hf:?}");
|
|
assert_eq!(got, hf, "Q8 must match HF on Paris (max_new=8)");
|
|
}
|
|
|
|
/// Per-group int8 (Q8G) — the production tier. Token-identical to HF
|
|
/// on both Paris AND the long Jupiter prompt (where per-row Q8 drifts).
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_q8g_token_identical() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let g = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load f32");
|
|
let q = jouleclaw_loader_gguf::gemma4_q8g::Gemma4Q8G::from_gemma4(&g);
|
|
|
|
let paris_p: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let paris_hf: Vec<u32> = vec![9079, 236761, 108, 818, 5279, 529, 7001, 563];
|
|
let got_paris = q.generate_cached(&paris_p, 8);
|
|
assert_eq!(got_paris, paris_hf, "Q8G must match HF on Paris (max_new=8)");
|
|
|
|
let jup_p: Vec<u32> = vec![2, 818, 7488, 13401, 528, 1023, 10321, 1458, 563];
|
|
let jup_hf: Vec<u32> = vec![52895, 236761, 1030, 563, 496, 4314, 16784, 236764];
|
|
let got_jup = q.generate_cached(&jup_p, 8);
|
|
assert_eq!(got_jup, jup_hf, "Q8G must match HF on Jupiter (max_new=8)");
|
|
}
|
|
|
|
/// Asymmetric per-row per-group int5 (Q5) — the working int4-class
|
|
/// quantization. Token-identical to HF f32 on Paris at max_new=8,
|
|
/// ~0.78 B/param (between Q4's 0.6 and Q8G's 1.08).
|
|
#[test]
|
|
#[ignore]
|
|
fn real_gemma4_e2b_q5_token_identical() {
|
|
if !std::path::Path::new(DIR).join("config.json").exists() {
|
|
eprintln!("skip: gemma-4-E2B not downloaded");
|
|
return;
|
|
}
|
|
let g = jouleclaw_loader_gguf::gemma4::Gemma4::load(DIR).expect("load f32");
|
|
let q5 = jouleclaw_loader_gguf::gemma4_q5::Gemma4Q5::from_gemma4(&g);
|
|
let prompt: Vec<u32> = vec![2, 818, 5279, 529, 7001, 563];
|
|
let hf: Vec<u32> = vec![9079, 236761, 108, 818, 5279, 529, 7001, 563];
|
|
let got = q5.generate_cached(&prompt, 8);
|
|
eprintln!("Q5 +8: {got:?}");
|
|
eprintln!("HF : {hf:?}");
|
|
assert_eq!(got, hf, "Q5 must match HF on Paris (max_new=8)");
|
|
}
|