open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-runtime/tests/rope_and_scaling.rs
Transaction Science c0c405e25a jouleclaw: workspace audit — zero warnings, zero errors, 3096/3096 tests
Full review of the JouleClaw Rust workspace (120 crates) to deliver
"no errors, no warnings, no stubs, no code islands" as of 2026-06-03.

Mechanical state, post-audit:
- cargo build --workspace --all-targets: 0 errors, 0 warnings
- cargo test --workspace --no-fail-fast: 3096 pass, 0 fail, 57 ignored
- 0 stubs / unimplemented! in production code paths (the 2 hits are
  in test helpers, both explicitly scoped)
- 0 genuine code islands (40 leaf crates are all legit: binaries,
  standalone end-user surfaces, or recent waves)

Files touched in the cleanup:
- jouleclaw-omni (Cargo.toml + src/lib.rs): vendored efficient-genai
  port — added #![allow(missing_docs)] at crate root with documented
  reason (we did not author 100k+ LOC of ML internals; the PUBLIC
  surface is documented at module level). Also added [lints.rust]
  check-cfg for the dropped `jouledb` feature flag so the gate
  doesn't surface as `unexpected_cfgs`.
- jouleclaw-edge: restored `use std::sync::Arc` in the test module
  (cargo-fix regression — Arc is used in a #[cfg(test)] block).
- jouleclaw-attest, -prov, -mcp, -federation, -sketch, -gate,
  -loader-gguf, -agents-md, -workspace: added rustdoc comments on
  every previously-undocumented public item (struct fields, methods,
  enum variants, associated functions, trait methods). 291 tests
  across those crates verified green.
- jouleclaw-loader-gguf: removed unnecessary parentheses around two
  assigned bit-mask values.
- jouleclaw-agents-md: registered `cfg(kani)` via [lints.rust]
  check-cfg so the kani-proof markers don't surface as warnings.
- jouleclaw-workspace: marked EntryKind::wire as #[allow(dead_code)]
  (used by tests at lines 782–784).
- Workspace-wide cargo fix + clippy --fix sweep: knocked out
  is_multiple_of, div_ceil, collapsible-if, unused imports, unused
  variables, unused mut, and similar lints across many crates.

The jouleclaw-omni vendored port carries internal TODOs from the
upstream `efficient-genai` repo (HAL/CUDA/Cranelift/RoPE/flash-attn
scaffolding) — those are documented incomplete paths, not
JouleClaw's own stubs. The PUBLIC JouleClaw surface (cascade,
receipts, agents, contract, mission, verify, policy, workspace,
hydra, …) has zero stubs and zero TODOs.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-06-03 09:35:09 -04:00

225 lines
8.5 KiB
Rust

//! Tests for RoPE (rotary position embedding) and scaled MatMul.
use jouleclaw_core::graph::GraphBuilder;
use jouleclaw_core::tensor::{Dtype, Tensor, TensorMeta};
use jouleclaw_runtime::{compile, execute, ExecutionOptions, Runtime};
use std::collections::HashMap;
fn run_rope(
input: Vec<f32>,
shape: &[usize],
base: f32,
pos_offset: u32,
) -> Vec<f32> {
let mut g = GraphBuilder::new();
let in_meta = TensorMeta::new(Dtype::F32, shape);
let x = g.input("x", in_meta.clone());
let y = g.rope(x, base, pos_offset);
g.output("y", y);
let graph = g.build();
let runtime = Runtime::boot();
let compiled = compile(graph, &runtime.kernels).unwrap();
let mut inputs = HashMap::new();
inputs.insert("x".into(), Tensor::from_f32(in_meta, &input));
let res = execute(&compiled, inputs, ExecutionOptions::default()).unwrap();
res.outputs.get("y").unwrap().as_f32_vec()
}
/// At position 0 the angle is 0, so cos=1, sin=0, rotation = identity.
#[test]
fn rope_at_position_zero_is_identity() {
let _input = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; // [seq=2, d=4]
// But we want position 0 only — seq=1.
let input = vec![1.0, 2.0, 3.0, 4.0]; // [seq=1, d=4]
let out = run_rope(input.clone(), &[1, 4], 10000.0, 0);
// Position 0 always gives identity for any base.
assert_eq!(out, input);
}
/// Hand-computed test: d=2, base=10000, pos=1.
/// theta_0 = 10000^0 = 1, m = 1*1 = 1 radian.
/// Input [1, 0]: out = [cos(1), sin(1)] ≈ [0.5403, 0.8415]
/// Input [0, 1]: out = [-sin(1), cos(1)] ≈ [-0.8415, 0.5403]
#[test]
fn rope_d2_position_1_hand_computed() {
// Two-token sequence: token at pos 0 is identity, token at pos 1 rotates.
let input = vec![1.0, 0.0, 0.0, 1.0]; // [seq=2, d=2]
let out = run_rope(input, &[2, 2], 10000.0, 0);
// Token 0 at pos 0: identity.
assert!((out[0] - 1.0).abs() < 1e-6);
assert!((out[1] - 0.0).abs() < 1e-6);
// Token 1 at pos 1: rotation by 1 radian.
let cos1 = 1.0_f32.cos();
let sin1 = 1.0_f32.sin();
assert!((out[2] - 0.0 * cos1 + 1.0 * sin1).abs() < 1e-6,
"out[2] = 0*cos(1) - 1*sin(1) = -sin(1), got {}", out[2]);
assert!((out[3] - (0.0 * sin1 + 1.0 * cos1)).abs() < 1e-6,
"out[3] = 0*sin(1) + 1*cos(1) = cos(1), got {}", out[3]);
}
/// Position offset shifts the effective sequence position.
#[test]
fn rope_position_offset_works() {
let input = vec![1.0, 0.0]; // [seq=1, d=2]
// No offset: pos=0, identity.
let out0 = run_rope(input.clone(), &[1, 2], 10000.0, 0);
assert!((out0[0] - 1.0).abs() < 1e-6);
assert!((out0[1] - 0.0).abs() < 1e-6);
// Offset 1: same input behaves as if at pos 1, rotation by 1 radian.
let out1 = run_rope(input, &[1, 2], 10000.0, 1);
assert!((out1[0] - 1.0_f32.cos()).abs() < 1e-6,
"expected cos(1), got {}", out1[0]);
assert!((out1[1] - 1.0_f32.sin()).abs() < 1e-6,
"expected sin(1), got {}", out1[1]);
}
/// Multi-head shape works: [n_heads, seq, d_head] with d_head even.
#[test]
fn rope_handles_multihead_shape() {
let n_heads = 2;
let seq = 3;
let d_head = 4;
let total = n_heads * seq * d_head;
let input: Vec<f32> = (0..total).map(|i| i as f32 * 0.1).collect();
let out = run_rope(input.clone(), &[n_heads, seq, d_head], 10000.0, 0);
// Check: position-0 row of each head should be identity.
// Head 0, token 0 starts at offset 0.
assert!((out[0] - input[0]).abs() < 1e-6);
assert!((out[1] - input[1]).abs() < 1e-6);
assert!((out[2] - input[2]).abs() < 1e-6);
assert!((out[3] - input[3]).abs() < 1e-6);
// Head 1, token 0 starts at offset seq * d_head = 12.
let _h1t0 = n_heads * d_head; // wait, that's 8. Recompute: head 1 starts at seq*d_head = 12.
let h1t0 = seq * d_head;
assert!((out[h1t0 + 0] - input[h1t0 + 0]).abs() < 1e-6);
assert!((out[h1t0 + 1] - input[h1t0 + 1]).abs() < 1e-6);
assert!((out[h1t0 + 2] - input[h1t0 + 2]).abs() < 1e-6);
assert!((out[h1t0 + 3] - input[h1t0 + 3]).abs() < 1e-6);
// Output shape is preserved.
assert_eq!(out.len(), total);
}
/// Determinism: running RoPE twice must produce byte-identical results.
#[test]
fn rope_is_deterministic() {
let input = vec![0.5, -0.3, 0.7, 0.1];
let out1 = run_rope(input.clone(), &[2, 2], 10000.0, 0);
let out2 = run_rope(input, &[2, 2], 10000.0, 0);
assert_eq!(out1, out2, "rope must be byte-identical across runs");
}
/// Each pair rotates by a different angle: theta_i = base^(-2i/d).
/// For d=4, base=100, theta_0 = 1, theta_1 = 100^(-1/2) = 0.1.
/// At pos=1: pair (0,1) rotates by 1 rad, pair (2,3) rotates by 0.1 rad.
#[test]
fn rope_different_pairs_use_different_angles() {
// Two tokens, d=4. Token 0 at pos 0 is identity; token 1 at pos 1
// rotates each pair by its own theta.
let input = vec![
1.0, 0.0, 1.0, 0.0, // token 0
1.0, 0.0, 1.0, 0.0, // token 1
];
let out = run_rope(input, &[2, 4], 100.0, 0);
// Token 0: identity.
assert!((out[0] - 1.0).abs() < 1e-6);
assert!((out[1] - 0.0).abs() < 1e-6);
assert!((out[2] - 1.0).abs() < 1e-6);
assert!((out[3] - 0.0).abs() < 1e-6);
// Token 1, pair (0,1): rotation by 1 rad.
// x=[1,0] -> [cos(1), sin(1)]
let cos1 = 1.0_f32.cos();
let sin1 = 1.0_f32.sin();
assert!((out[4] - cos1).abs() < 1e-6);
assert!((out[5] - sin1).abs() < 1e-6);
// Token 1, pair (2,3): theta = 100^(-2/4) = 100^(-0.5) = 0.1.
// angle = 1 * 0.1 = 0.1 rad.
// x=[1,0] -> [cos(0.1), sin(0.1)]
let cos01 = 0.1_f32.cos();
let sin01 = 0.1_f32.sin();
assert!((out[6] - cos01).abs() < 1e-6,
"expected cos(0.1) = {}, got {}", cos01, out[6]);
assert!((out[7] - sin01).abs() < 1e-6,
"expected sin(0.1) = {}, got {}", sin01, out[7]);
}
// =====================================================================
// Attention scaling (alpha in MatMul)
// =====================================================================
/// matmul_bt_scaled multiplies the output by alpha.
#[test]
fn matmul_bt_scaled_applies_alpha() {
let mut g = GraphBuilder::new();
let a = g.input("a", TensorMeta::new(Dtype::F32, &[2, 4]));
let b = g.input("b", TensorMeta::new(Dtype::F32, &[3, 4]));
let scaled = g.matmul_bt_scaled(a, b, 0.5);
let unscaled = g.matmul_bt(a, b);
g.output("scaled", scaled);
g.output("unscaled", unscaled);
let graph = g.build();
let runtime = Runtime::boot();
let compiled = compile(graph, &runtime.kernels).unwrap();
let a_data: Vec<f32> = (0..8).map(|i| i as f32 + 1.0).collect();
let b_data: Vec<f32> = (0..12).map(|i| i as f32 * 0.5).collect();
let mut inputs = HashMap::new();
inputs.insert("a".into(),
Tensor::from_f32(TensorMeta::new(Dtype::F32, &[2, 4]), &a_data));
inputs.insert("b".into(),
Tensor::from_f32(TensorMeta::new(Dtype::F32, &[3, 4]), &b_data));
let res = execute(&compiled, inputs, ExecutionOptions::default()).unwrap();
let scaled = res.outputs.get("scaled").unwrap().as_f32_vec();
let unscaled = res.outputs.get("unscaled").unwrap().as_f32_vec();
assert_eq!(scaled.len(), unscaled.len());
for (s, u) in scaled.iter().zip(unscaled.iter()) {
assert!((s - u * 0.5).abs() < 1e-5,
"scaled output should be 0.5 * unscaled; got {} vs {}", s, u);
}
}
/// Alpha = 1.0 is the default and matches matmul (without scaling).
#[test]
fn alpha_one_matches_default_matmul() {
let mut g = GraphBuilder::new();
let a = g.input("a", TensorMeta::new(Dtype::F32, &[3, 5]));
let b = g.input("b", TensorMeta::new(Dtype::F32, &[5, 4]));
let default = g.matmul(a, b);
let alpha_one = g.matmul_with_alpha(a, b, false, false, 1.0);
g.output("default", default);
g.output("alpha_one", alpha_one);
let graph = g.build();
let runtime = Runtime::boot();
let compiled = compile(graph, &runtime.kernels).unwrap();
let a_data: Vec<f32> = (0..15).map(|i| i as f32 * 0.1).collect();
let b_data: Vec<f32> = (0..20).map(|i| i as f32 * 0.05).collect();
let mut inputs = HashMap::new();
inputs.insert("a".into(),
Tensor::from_f32(TensorMeta::new(Dtype::F32, &[3, 5]), &a_data));
inputs.insert("b".into(),
Tensor::from_f32(TensorMeta::new(Dtype::F32, &[5, 4]), &b_data));
let res = execute(&compiled, inputs, ExecutionOptions::default()).unwrap();
let default = res.outputs.get("default").unwrap().as_f32_vec();
let alpha_one = res.outputs.get("alpha_one").unwrap().as_f32_vec();
assert_eq!(default, alpha_one,
"alpha=1.0 must produce bit-identical output to default matmul");
}