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]>
106 lines
3.5 KiB
Rust
106 lines
3.5 KiB
Rust
//! Sampling tests.
|
|
|
|
use jouleclaw_loader_gguf::sample::{sample_logits, SamplingConfig};
|
|
|
|
/// Greedy sampling picks the argmax with low-index tie-break.
|
|
#[test]
|
|
fn greedy_picks_argmax() {
|
|
let logits = vec![0.1, -1.0, 0.5, 2.5, 1.7];
|
|
let id = sample_logits(&logits, &SamplingConfig::greedy());
|
|
assert_eq!(id, 3);
|
|
}
|
|
|
|
/// Greedy with ties: lower index wins.
|
|
#[test]
|
|
fn greedy_tie_break_lower_index() {
|
|
let logits = vec![1.0, 1.0, 1.0, 0.5];
|
|
let id = sample_logits(&logits, &SamplingConfig::greedy());
|
|
assert_eq!(id, 0, "expected lowest index 0 on three-way tie");
|
|
}
|
|
|
|
/// Temperature sampling is deterministic given a seed.
|
|
#[test]
|
|
fn temperature_seeded_is_deterministic() {
|
|
let logits = vec![0.5, 1.0, 1.5, 2.0, 1.8, 0.3];
|
|
let cfg = SamplingConfig::temperature(0.8, 42);
|
|
let id1 = sample_logits(&logits, &cfg);
|
|
let id2 = sample_logits(&logits, &cfg);
|
|
let id3 = sample_logits(&logits, &cfg);
|
|
assert_eq!(id1, id2);
|
|
assert_eq!(id2, id3);
|
|
}
|
|
|
|
/// Different seeds can produce different samples.
|
|
#[test]
|
|
fn different_seeds_can_diverge() {
|
|
let logits = vec![0.5, 1.0, 1.5, 2.0, 1.8, 0.3];
|
|
let mut samples = std::collections::HashSet::new();
|
|
for seed in 0..50 {
|
|
let cfg = SamplingConfig::temperature(2.0, seed);
|
|
samples.insert(sample_logits(&logits, &cfg));
|
|
}
|
|
assert!(samples.len() > 1,
|
|
"high-temperature sampling over 50 seeds should produce >1 distinct token");
|
|
}
|
|
|
|
/// Top-K filter restricts the selection set.
|
|
#[test]
|
|
fn top_k_only_samples_from_top_k() {
|
|
// Logits: idx 0 has score 100 (so high it dominates anything past it
|
|
// in any reasonable softmax), idx 1 = 50, rest negative.
|
|
let mut logits = vec![-100.0_f32; 32];
|
|
logits[0] = 100.0;
|
|
logits[1] = 50.0;
|
|
|
|
// top_k=2 with high temperature: must always sample 0 or 1.
|
|
for seed in 0..30 {
|
|
let cfg = SamplingConfig::top_k(2, 1.0, seed);
|
|
let id = sample_logits(&logits, &cfg);
|
|
assert!(id == 0 || id == 1,
|
|
"top-k=2 should sample from idx 0 or 1, got {}", id);
|
|
}
|
|
}
|
|
|
|
/// Top-K=1 collapses to greedy regardless of temperature/seed.
|
|
#[test]
|
|
fn top_k_one_is_greedy() {
|
|
let logits = vec![0.5, 2.0, 1.0, 1.5];
|
|
for seed in 0..10 {
|
|
let cfg = SamplingConfig::top_k(1, 5.0, seed);
|
|
let id = sample_logits(&logits, &cfg);
|
|
assert_eq!(id, 1, "top-k=1 should always pick argmax (1)");
|
|
}
|
|
}
|
|
|
|
/// Top-P (nucleus) filter takes the smallest set whose cumulative
|
|
/// probability covers the threshold.
|
|
#[test]
|
|
fn top_p_truncates_long_tail() {
|
|
// Two dominant tokens that together cover ~95% of mass; rest are
|
|
// long-tail noise.
|
|
let mut logits = vec![-10.0_f32; 100];
|
|
logits[0] = 5.0;
|
|
logits[1] = 4.5;
|
|
|
|
// top_p = 0.5 should always select from {0, 1}.
|
|
for seed in 0..30 {
|
|
let cfg = SamplingConfig::top_p(0.5, 1.0, seed);
|
|
let id = sample_logits(&logits, &cfg);
|
|
assert!(id == 0 || id == 1,
|
|
"top-p=0.5 with two dominant tokens should sample from {{0, 1}}, got {}", id);
|
|
}
|
|
}
|
|
|
|
/// Greedy is unaffected by temperature/seed since it short-circuits at T=0.
|
|
#[test]
|
|
fn greedy_ignores_seed() {
|
|
let logits = vec![0.1, 0.5, 0.3, 0.9, 0.7];
|
|
let id1 = sample_logits(&logits, &SamplingConfig {
|
|
temperature: 0.0, top_k: 0, top_p: 1.0, seed: 1, ..Default::default()
|
|
});
|
|
let id2 = sample_logits(&logits, &SamplingConfig {
|
|
temperature: 0.0, top_k: 0, top_p: 1.0, seed: 999999, ..Default::default()
|
|
});
|
|
assert_eq!(id1, id2);
|
|
assert_eq!(id1, 3);
|
|
}
|