From 450883d1e51755d8d2941d491a1be35bb3d143aa Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 11:46:09 +0000 Subject: [PATCH 1/2] hpc: docstring the randomized_signature tests and bench MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CodeRabbit's docstring-coverage check on PR #294 reported 61.76% against an 80% threshold, scoped to the functions touched by that diff (34 functions across 3 files). The gap was entirely in test and bench code: the three public functions and the four private SIMD/validation helpers were already documented, but the SplitMix64 methods, the two test helpers, ten of the eleven test bodies, and eight of the nine bench functions were not. Adds /// docs to all of them. Where a test already carried an explanatory inline // block (zero_increment, sub_epsilon, custom_activation), the prose is promoted to the docstring rather than duplicated, so each rationale is stated once. The docs record why each test discriminates, not just what it calls — the lane-boundary widths that catch a mishandled remainder loop, why the constant-path test is weak without its supra-epsilon pair, and why the bench's path wobble keeps every increment clear of the 1e-15 skip so both implementations do the full O(T*d*k^2) of work being compared. No behavioural change; comments only. Both files now measure 100% documented. The seven remaining undocumented functions in src/hpc/mod.rs are pre-existing pipeline tests that the #294 diff never touched, so they were outside the check's scope and are left alone. Verified: cargo fmt --check clean; cargo clippy --release --lib --tests --example randomized_signature_bench -- -D warnings clean; cargo test --release --lib 2270 passed / 0 failed; doctests 3 passed. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016WkNBjHc2e3zuyz9i8qJEv --- examples/randomized_signature_bench.rs | 38 ++++++++++ src/hpc/randomized_signature.rs | 98 +++++++++++++++++++++----- 2 files changed, 118 insertions(+), 18 deletions(-) diff --git a/examples/randomized_signature_bench.rs b/examples/randomized_signature_bench.rs index 11f23b6f..cb02f999 100644 --- a/examples/randomized_signature_bench.rs +++ b/examples/randomized_signature_bench.rs @@ -19,6 +19,9 @@ use std::time::Instant; struct SplitMix64(u64); impl SplitMix64 { + /// One raw 64-bit draw, advancing the state by the SplitMix64 gamma + /// constant. Deterministic given the seed, so every run of this bench + /// measures the same projections and the same path. fn next_u64(&mut self) -> u64 { self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15); let mut z = self.0; @@ -26,9 +29,13 @@ impl SplitMix64 { z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); z ^ (z >> 31) } + /// A draw in `[0, 1)`, taking the top 53 bits so every value is exactly + /// representable in `f64`. fn uniform(&mut self) -> f64 { (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64 } + /// A standard-normal draw via Box-Muller (cosine branch), clamping `u1` + /// away from zero so `ln` never returns `-inf`. fn normal(&mut self) -> f64 { let u1 = self.uniform().max(1e-300); let u2 = self.uniform(); @@ -36,6 +43,10 @@ impl SplitMix64 { } } +/// Build the `(matrices, biases)` pair for a `d`-dimensional path and a `k` +/// -wide state: `d` stacked `k×k` Gaussian blocks plus `d` bias vectors of +/// length `k`, all scaled by `1/sqrt(k)` — the builder's own recipe, so the +/// benchmarked operand magnitudes match the consumer's. fn projections(d: usize, k: usize, seed: u64) -> (Vec, Vec) { let scale = (k as f64).recip().sqrt(); let mut rng = SplitMix64(seed); @@ -44,6 +55,12 @@ fn projections(d: usize, k: usize, seed: u64) -> (Vec, Vec) { (matrices, biases) } +/// A `t`-step, `d`-dimensional benchmark path: a linear ramp per coordinate +/// plus a small out-of-phase cosine wobble. +/// +/// The wobble matters for timing, not just realism — every increment stays +/// clear of the `1e-15` skip threshold, so no step is short-circuited and +/// both implementations do the full `O(T·d·k²)` of work being compared. fn path(t: usize, d: usize, seed: f64) -> Vec> { (0..=t) .map(|i| { @@ -94,6 +111,12 @@ fn scalar_encode(path: &[Vec], matrices: &[f64], biases: &[f64], k: usize) z } +/// Max relative error between two states, with the denominator floored at +/// `1.0` so near-zero rows report absolute rather than exploding error. +/// +/// Reported alongside every timing row: a speedup is only meaningful if the +/// two implementations still agree, and this is what makes a "fast but wrong" +/// kernel visible in the bench output instead of silently applauded. fn rel_err(a: &[f64], b: &[f64]) -> f64 { a.iter() .zip(b.iter()) @@ -101,6 +124,13 @@ fn rel_err(a: &[f64], b: &[f64]) -> f64 { .fold(0.0f64, f64::max) } +/// Time one `(T, d, k)` shape both ways and print the row: scalar seconds, +/// SIMD seconds, speedup, and max relative error. +/// +/// Single-shot timing, not a statistical harness — the shapes here run long +/// enough (milliseconds and up) that run-to-run noise stays well below the +/// effect being reported. Both implementations see the identical projections +/// and path, so the comparison is like-for-like. fn bench_one(t: usize, d: usize, k: usize) { let (matrices, biases) = projections(d, k, 0xBEEF); let p = path(t, d, 0.3); @@ -121,6 +151,14 @@ fn bench_one(t: usize, d: usize, k: usize) { ); } +/// Sweep the state widths `k = 32 … 512` against the scalar baseline, then +/// run sigker's headline envelope shape (`T = 64, d = 8, k = 4096`) +/// SIMD-only and report its achieved GFLOP/s. +/// +/// The envelope row omits a scalar baseline deliberately: at `k = 4096` the +/// row-major loop runs for minutes, and the row is there to show the shape is +/// reachable at all, not to claim a speedup. Its `|z|_inf` is printed so the +/// result is visibly finite rather than a NaN that timed well. fn main() { println!("== randomized_signature_sweep — SIMD GEMV+axpy vs. row-major scalar ==\n"); diff --git a/src/hpc/randomized_signature.rs b/src/hpc/randomized_signature.rs index 5bd4c144..f7465a86 100644 --- a/src/hpc/randomized_signature.rs +++ b/src/hpc/randomized_signature.rs @@ -345,6 +345,9 @@ mod tests { struct SplitMix64(u64); impl SplitMix64 { + /// One raw 64-bit draw, advancing the state by the SplitMix64 gamma + /// constant. Deterministic given the seed — every corpus below is + /// reproducible from its seed alone. fn next_u64(&mut self) -> u64 { self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15); let mut z = self.0; @@ -352,9 +355,13 @@ mod tests { z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); z ^ (z >> 31) } + /// A draw in `[0, 1)`, taking the top 53 bits so every value is + /// exactly representable in `f64`. fn uniform(&mut self) -> f64 { (self.next_u64() >> 11) as f64 / (1u64 << 53) as f64 } + /// A standard-normal draw via Box-Muller (cosine branch), clamping + /// `u1` away from zero so `ln` never returns `-inf`. fn normal(&mut self) -> f64 { let u1 = self.uniform().max(1e-300); let u2 = self.uniform(); @@ -371,6 +378,10 @@ mod tests { (matrices, biases) } + /// A `t`-step, `d`-dimensional test path: a linear ramp per coordinate + /// plus a small out-of-phase cosine wobble, so consecutive increments are + /// non-zero, non-constant, and differ across coordinates. `seed` shifts + /// the phase, giving independent-looking paths without an RNG. fn wiggly_path(t: usize, d: usize, seed: f64) -> Vec> { (0..=t) .map(|i| { @@ -385,6 +396,15 @@ mod tests { .collect() } + /// The parity harness: run `path` through both the SIMD sweep and the + /// scalar oracle over the same seeded projections and assert they agree + /// row-wise to the module's declared `1e-9` relative tolerance. + /// + /// The tolerance is a contract, not a fudge factor — the SIMD GEMV + /// reduces eight partial sums and fuses its products, so it is not + /// bit-identical to the oracle by construction (see the module docs). + /// Also asserts the returned width is `state_dim`, so a silently + /// truncated state cannot pass by matching on a prefix. fn assert_matches_reference(path: &[Vec], d: usize, k: usize, seed: u64) { let (matrices, biases) = projections(d, k, seed); let expected = scalar_reference(path, &matrices, &biases, k); @@ -401,14 +421,22 @@ mod tests { } } + /// Parity with the scalar oracle across state widths that straddle the + /// `F64x8` lane boundary in both directions — `1, 7` (under), `8, 16, 64` + /// (exact multiples), `9, 33` (one past). This is the test that catches a + /// mishandled remainder loop: a kernel that only walked whole lanes would + /// pass at `k = 8` and fail at `k = 9`. #[test] fn parity_across_state_widths() { - // Widths straddling the lane boundary in both directions. for &k in &[1usize, 7, 8, 9, 16, 33, 64] { assert_matches_reference(&wiggly_path(12, 3, 0.4), 3, k, 0xDEAD_BEEF); } } + /// Parity across path dimensions `d`, the axis that selects which of the + /// `d` stacked `k×k` blocks of `matrices` a step reads. Fixes `k` and + /// varies `d` alone, so a wrong `i * k * k` block stride shows up here + /// rather than hiding behind a state-width failure. #[test] fn parity_across_path_dimensions() { for &d in &[1usize, 2, 5, 9] { @@ -416,10 +444,12 @@ mod tests { } } + /// Parity over 60 distinct `(seed, T, d, k)` draws — the consumer + /// contract's "hand-roll 50+ inputs" corpus, every case carrying a fresh + /// Gaussian projection. Fixed root seed, so a failure names a + /// reproducible case rather than a flake. #[test] fn parity_over_seeded_corpus() { - // 60 distinct (seed, T, d, k) draws — the contract's "hand-roll 50+ - // inputs" corpus, every one of them a fresh Gaussian projection. let mut rng = SplitMix64(0xC0FF_EE00); for case in 0..60u64 { let t = 2 + (rng.next_u64() % 9) as usize; @@ -429,12 +459,19 @@ mod tests { } } + /// Parity on a case that is past one lane in every dimension with no + /// dimension a multiple of 8 (`T = 37`, `k = 67`): enough accumulated + /// steps for a drifting reduction order to exceed tolerance, and a + /// remainder tail on every GEMV row. #[test] fn parity_with_long_path_and_wide_state() { - // Past one lane in every dimension, none of them a multiple of 8. assert_matches_reference(&wiggly_path(37, 3, 0.9), 3, 67, 7); } + /// A path with no increments — a single point, or empty — yields the zero + /// state rather than panicking. `windows(2)` is empty in both cases, so + /// `z` never leaves `z_0 = 0`; this pins that as documented behaviour and + /// not an accident of the loop shape. #[test] fn degenerate_single_point_path_is_zero_state() { let (matrices, biases) = projections(3, 11, 5); @@ -444,26 +481,31 @@ mod tests { assert_eq!(randomized_signature_sweep(&empty, &matrices, &biases, 11), vec![0.0; 11]); } + /// A constant path has `|dx_i| = 0 < 1e-15` on every coordinate, so every + /// GEMV is skipped and `z` never leaves `z_0 = 0`. The invariant is + /// hand-derived from the skip rule, not read off the code under test. + /// + /// On its own this test is weak — drop the skip entirely and the result + /// is still zero here, because `dx = 0` scales the axpy to nothing. What + /// makes the guard falsifiable is the paired supra-epsilon half in + /// [`sub_epsilon_increment_is_skipped_but_supra_epsilon_is_not`]. #[test] fn zero_increment_path_leaves_state_at_zero() { - // A constant path has |dx_i| = 0 < 1e-15 on every coordinate, so - // every GEMV is skipped and z never leaves z_0 = 0. This is a real - // invariant of the recurrence (hand-derived from the skip rule, not - // read off the code under test) and it discriminates: drop the skip - // and the result is still 0 here, but *flip the skip into an - // unconditional apply with a non-zero dx* and it is not — which the - // next test covers. let (matrices, biases) = projections(2, 20, 11); let path: Vec> = (0..15).map(|_| vec![7.0, -3.0]).collect(); assert_eq!(randomized_signature_sweep(&path, &matrices, &biases, 20), vec![0.0; 20]); } + /// The [`INCREMENT_EPSILON`] guard must actually gate, in both + /// directions: an increment just under `1e-15` leaves the state + /// untouched, one just over it moves it. + /// + /// Both halves are required. A can-it-fire assertion alone would pass for + /// a primitive with no guard; a can-it-stay-silent assertion alone (the + /// constant-path test) would pass for one that skipped everything. Only + /// the pair proves the threshold discriminates. #[test] fn sub_epsilon_increment_is_skipped_but_supra_epsilon_is_not() { - // The 1e-15 guard must actually gate: an increment just under it - // leaves the state untouched, one just over it does not. Without - // both halves the constant-path test above would pass for a - // primitive that had no guard at all. let (matrices, biases) = projections(1, 12, 3); let below = vec![vec![0.0], vec![1e-16]]; let above = vec![vec![0.0], vec![1e-13]]; @@ -473,11 +515,16 @@ mod tests { assert!(z_above.iter().any(|v| *v != 0.0), "supra-epsilon increment must move the state"); } + /// The caller's activation closure is genuinely applied, not decoration + /// over a hard-wired `tanh`. + /// + /// With `sigma == identity` the first step collapses to `z_1 = b * dx` + /// exactly — `z_0 = 0`, so `A · z_0 = 0` — which is checkable in closed + /// form against `biases`. The second half then asserts `tanh` gives a + /// *different* answer on the same inputs, so the test cannot pass by the + /// two activations happening to agree. #[test] fn custom_activation_is_the_one_applied() { - // sigma == identity turns the first step into z_1 = b * dx exactly - // (z_0 = 0, so A . z_0 = 0), which is checkable in closed form — - // proving the closure is used rather than tanh being hard-wired. let (matrices, biases) = projections(1, 9, 17); let path = vec![vec![0.0], vec![2.0]]; let z = randomized_signature_sweep_with(&path, &matrices, &biases, 9, |x| x); @@ -492,6 +539,13 @@ mod tests { .any(|(a, b)| (a - b).abs() > 1e-9)); } + /// The two public entry points agree: [`randomized_signature_step`] from + /// the zero state reproduces [`randomized_signature_sweep`] over a + /// two-point path with the same increment, bit for bit. + /// + /// Asserted with `assert_eq!` rather than a tolerance — both paths run + /// the identical `step_into` kernel, so any drift here means the sweep + /// wrapper has diverged from the step wrapper, not a numerics issue. #[test] fn step_matches_one_sweep_iteration() { let (matrices, biases) = projections(2, 13, 23); @@ -501,12 +555,20 @@ mod tests { assert_eq!(swept, stepped); } + /// A `matrices` buffer that is not `d * k * k` long panics with a message + /// naming the field, instead of silently indexing a short buffer or + /// inferring `k` from the length it happens to have. Lengths are checked, + /// never inferred. #[test] #[should_panic(expected = "matrices must hold")] fn wrong_matrix_length_panics() { let _ = randomized_signature_sweep(&[vec![0.0], vec![1.0]], &[0.0; 3], &[0.0; 2], 2); } + /// The `biases` counterpart to [`wrong_matrix_length_panics`]: a correct + /// `matrices` with a short `biases` must still be caught, so the two + /// length checks are separately falsifiable rather than one guard that + /// happens to cover both. #[test] #[should_panic(expected = "biases must hold")] fn wrong_bias_length_panics() { From 68b138e27719dff7077aeb1b47ffd2843350d4b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 3 Sep 2026 11:58:42 +0000 Subject: [PATCH 2/2] hpc: docstring the e2e pipeline tests in hpc/mod.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Completes the docstring sweep started in the previous commit. These seven `e2e_tests` functions were outside CodeRabbit's diff-scoped coverage check on PR #294 (the #294 diff only added a `pub mod` line to this file, so they counted as untouched), but they were the remaining undocumented functions in the crate's hpc module surface. Each doc names what the pipeline stage chain establishes and why the test discriminates, rather than restating the call sequence: - fingerprint_to_node_to_seal: the seal half is the discriminating one — Wisdom against its own root, Staunen after one further encounter. - cascade_search: the query IS database[0], so an exact self-hit at Hamming 0 is the falsifiable minimum. - clam_knn: one-hot data makes every non-self distance uniform (16 bits), so the ranking has a single unambiguous winner; root().cardinality == n is the coverage check that !nodes.is_empty() would miss. - causality_decomposition: three channels with mixed signs, so a decomposition applying one direction uniformly matches at most 2 of 3. - bnn_inference: both poles pin the whole affine match-count-to-score map; either alone would admit a wrong scale or offset. - blackboard_arena: asserts a present AND an absent key, so `contains` cannot pass by always returning true. - full_e2e: pins that the stages compose on one node pair, where the others each pin a single stage. No behavioural change; comments only. The whole crate now measures 100% documented across these three files (42/42 functions). Verified: cargo fmt --check clean; cargo clippy --release --lib --tests --example randomized_signature_bench -- -D warnings clean; cargo test --release --lib 2270 passed / 0 failed; the 7 tests themselves confirmed running and green under the default `hpc-extras` feature. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_016WkNBjHc2e3zuyz9i8qJEv --- src/hpc/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/hpc/mod.rs b/src/hpc/mod.rs index 8e6f8f34..4d7df139 100644 --- a/src/hpc/mod.rs +++ b/src/hpc/mod.rs @@ -327,6 +327,15 @@ mod e2e_tests { use super::node::{Node, _P_, __O, SPO, S__}; use super::seal::Seal; + /// Fingerprint -> Node -> Seal: two independently seeded nodes are + /// *comparable* (non-zero overlap) and *distinct* (non-zero disagreement), + /// and a plane's Merkle root detects mutation. + /// + /// The seal half is the discriminating one. `verify` returns `Wisdom` against + /// the root it was built from, then `Staunen` after a single further + /// `encounter` -- so a `merkle()` that ignored later writes, or a `verify` + /// that always agreed, fails here. The plane is rebuilt from scratch rather + /// than reusing the nodes above, to keep the root deterministic. #[test] fn pipeline_fingerprint_to_node_to_seal() { // 1. Create two nodes and accumulate evidence @@ -357,6 +366,13 @@ mod e2e_tests { assert_eq!(p.verify(&root), Seal::Staunen); } + /// Cascade search over a packed database of 50 random 256-byte fingerprints + /// must find the query itself at Hamming distance 0. + /// + /// The query *is* `database[0]`, so an exact self-hit is the falsifiable + /// minimum: a cascade whose band thresholds rejected the foveal band, or + /// whose stride walked the packed buffer at the wrong vector width, would + /// return results that never contain `index == 0`. #[test] fn pipeline_cascade_search() { let vec_bytes = 256; @@ -384,6 +400,15 @@ mod e2e_tests { assert!(results.iter().any(|r| r.index == 0 && r.hamming == 0)); } + /// CLAM k-NN over one-hot vectors: brute force returns exactly `k` hits with + /// the self-match ranked first at distance 0, and `ClamTree::build` covers + /// the whole input. + /// + /// The data is deliberately one-hot (vector `i` sets byte `i % vec_len`), so + /// distances are uniform apart from the self-match and the ranking has a + /// single unambiguous winner. `root().cardinality == n` is the coverage + /// check -- a tree that dropped points during partitioning still builds + /// non-empty, so `!nodes.is_empty()` alone would not catch it. #[test] fn pipeline_clam_knn() { let vec_len = 32; @@ -409,6 +434,13 @@ mod e2e_tests { assert_eq!(tree.root().cardinality, n); } + /// Causality decomposition reads the *sign* of each qualia channel: + /// positive resonance decomposes to `Forward`, negative to `Backward`. + /// + /// Three channels are set with mixed signs (warmth +, social -, sacredness +) + /// against a zero baseline, so the test discriminates in a way a single + /// channel could not: a decomposition that ignored sign, or applied one + /// direction uniformly across channels, matches on at most two of the three. #[test] fn pipeline_causality_decomposition() { let mut a = PackedQualia::zero(); @@ -423,6 +455,12 @@ mod e2e_tests { assert_eq!(dec.sacredness_dir, CausalityDirection::Forward); } + /// Binary neural dot over 16384-bit fingerprints, pinned at both poles: + /// all-ones against all-ones is a full 16384 matches scoring `+1.0`, and + /// all-zeros against all-ones is 0 matches scoring `-1.0`. + /// + /// Testing both ends fixes the whole affine mapping from match count to + /// score. Either pole alone would admit a wrong scale or offset. #[test] fn pipeline_bnn_inference() { let act = Fingerprint::<256>::ones(); @@ -437,6 +475,13 @@ mod e2e_tests { assert!((result2.score - (-1.0)).abs() < 1e-6); } + /// Blackboard typed-slot arena: allocate two differently typed slots, mutate + /// each through `get_mut`, and read the mutations back through `get`. + /// + /// Both a present and an absent key are asserted against `contains`, so the + /// membership check cannot pass by unconditionally returning `true` -- and + /// the read-back is what proves `get_mut` hands out a reference into the + /// arena rather than a copy that is dropped. #[test] fn pipeline_blackboard_arena() { let mut bb = Blackboard::new(); @@ -458,6 +503,15 @@ mod e2e_tests { assert!(!bb.contains("nonexistent")); } + /// The whole chain in one pass: `Node` -> truth -> per-plane distance -> + /// Merkle seal -> cascade band -> BNN inference. + /// + /// Where the tests above each pin one stage, this one pins that the stages + /// *compose* on the same pair of nodes -- every plane mask (`S__`, `_P_`, + /// `__O`, `SPO`) yields `Measured` rather than `Incomparable`, and the + /// fingerprints that come out the far end still score strictly inside + /// `(-1, 1)`, which a stage that silently zeroed or saturated its output + /// would violate. #[test] fn pipeline_full_e2e() { // Full pipeline: Node → truth → causality → cascade → BNN