From 2b67e111e5194e50ffd3744ee1c18f37197a953c Mon Sep 17 00:00:00 2001 From: AdaWorldAPI Date: Tue, 27 Oct 2026 14:54:54 +0100 Subject: [PATCH 1/4] test(lab): add isolated signed-magnitude9 palette512 arithmetic oracle --- examples/palette512_probe.rs | 188 +++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 examples/palette512_probe.rs diff --git a/examples/palette512_probe.rs b/examples/palette512_probe.rs new file mode 100644 index 00000000..ecf87e06 --- /dev/null +++ b/examples/palette512_probe.rs @@ -0,0 +1,188 @@ +//! Palette512 / SignedMagnitude9 isolated arithmetic probe. +//! +//! This is NOT a 512-centroid codebook. It uses one u8 magnitude per lane +//! and a 64-bit sign plane for 64 lanes: 72 bytes total. Of 512 codewords, +//! sign=1,magnitude=0 is the sole NaN sentinel; the other 511 are numbers. +//! No production codec, public ABI, calibration, or wire format is changed. + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum ProbeError { + Nan, + Overflow, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct SignedMagnitude9 { + magnitude: u8, + negative: bool, +} + +impl SignedMagnitude9 { + /// Bit 8 is the sign; bits 0..7 are the magnitude. + fn from_code(code: u16) -> Self { + assert!(code < 512, "not a 9-bit codeword"); + Self { + magnitude: code as u8, + negative: (code & 0x100) != 0, + } + } + + fn code(self) -> u16 { + u16::from(self.magnitude) | ((self.negative as u16) << 8) + } + + fn value(self) -> Result { + if self.negative && self.magnitude == 0 { + return Err(ProbeError::Nan); + } + let value = i16::from(self.magnitude); + Ok(if self.negative { -value } else { value }) + } + + /// Handle sign in the accumulator without a signed-8-bit operand. + fn mul_add(self, rhs: Self, acc: i32) -> Result { + if (self.negative && self.magnitude == 0) + || (rhs.negative && rhs.magnitude == 0) + { + return Err(ProbeError::Nan); + } + let product = i32::from(self.magnitude) * i32::from(rhs.magnitude); + if self.negative ^ rhs.negative { + acc.checked_sub(product).ok_or(ProbeError::Overflow) + } else { + acc.checked_add(product).ok_or(ProbeError::Overflow) + } + } +} + +/// Bit i of signs belongs to lane i of magnitudes. +/// repr(C) pins the 72-byte in-memory footprint, NOT a persisted wire format. +#[repr(C)] +#[derive(Clone, Debug)] +struct MagnitudeSignBlock64 { + magnitudes: [u8; 64], + signs: u64, +} + +impl MagnitudeSignBlock64 { + fn from_codes(codes: &[u16; 64]) -> Self { + let mut magnitudes = [0u8; 64]; + let mut signs = 0u64; + for (i, &code) in codes.iter().enumerate() { + let lane = SignedMagnitude9::from_code(code); + magnitudes[i] = lane.magnitude; + if lane.negative { + signs |= 1u64 << i; + } + } + Self { magnitudes, signs } + } + + fn lane(&self, i: usize) -> SignedMagnitude9 { + SignedMagnitude9 { + magnitude: self.magnitudes[i], + negative: ((self.signs >> i) & 1) != 0, + } + } + + /// Scalar semantic reference for future SIMD lowering, not a speed claim. + fn fused_dot(&self, rhs: &Self) -> Result { + let mut acc = 0i32; + for i in 0..64 { + acc = self.lane(i).mul_add(rhs.lane(i), acc)?; + } + Ok(acc) + } +} + +fn main() { + println!("Palette512 SignedMagnitude9: run cargo test --example palette512_probe"); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn all_512_codewords_are_unique_and_one_is_nan() { + let mut valid = 0; + let mut nan = 0; + for code in 0..512u16 { + let lane = SignedMagnitude9::from_code(code); + assert_eq!(lane.code(), code); + match lane.value() { + Ok(v) => { + assert!((-255..=255).contains(&v)); + let expected = if code < 256 { code as i16 } else { -((code & 255) as i16) }; + assert_eq!(v, expected); + valid += 1; + } + Err(ProbeError::Nan) => { + assert_eq!(code, 0x100); + nan += 1; + } + Err(ProbeError::Overflow) => panic!("decoding cannot overflow"), + } + } + assert_eq!((valid, nan), (511, 1)); + assert_eq!(SignedMagnitude9::from_code(0).value(), Ok(0)); + assert_eq!(SignedMagnitude9::from_code(0x100).value(), Err(ProbeError::Nan)); + } + + #[test] + fn exhaustive_fused_mul_add_matches_independent_signed_oracle() { + for a_code in 0..512u16 { + for b_code in 0..512u16 { + let a = SignedMagnitude9::from_code(a_code); + let b = SignedMagnitude9::from_code(b_code); + let expected = match (a.value(), b.value()) { + (Ok(x), Ok(y)) => Ok(i32::from(x) * i32::from(y) + 17), + _ => Err(ProbeError::Nan), + }; + assert_eq!(a.mul_add(b, 17), expected, "a={a_code},b={b_code}"); + } + } + } + + #[test] + fn sixty_four_lane_sign_plane_matches_wide_reference() { + assert_eq!(std::mem::size_of::(), 72); + let lhs = std::array::from_fn(|i| match i % 4 { + 0 => 0x0FF, // +255 + 1 => 0x1FF, // -255 + 2 => 0x000, // +0 + _ => 0x100 | i as u16, // negative magnitudes 3..63 + }); + let rhs = std::array::from_fn(|i| match i % 3 { + 0 => 0x1FF, + 1 => 0x0FF, + _ => 0x000, + }); + let a = MagnitudeSignBlock64::from_codes(&lhs); + let b = MagnitudeSignBlock64::from_codes(&rhs); + let wide_reference: i64 = (0..64) + .map(|i| i64::from(a.lane(i).value().unwrap()) * i64::from(b.lane(i).value().unwrap())) + .sum(); + assert_eq!(a.fused_dot(&b), Ok(wide_reference as i32)); + assert_eq!(wide_reference, i64::from(wide_reference as i32)); + } + + #[test] + fn nan_propagates_even_when_other_operand_is_zero() { + let zeros = [0u16; 64]; + let mut negzero = zeros; + negzero[19] = 0x100; + let z = MagnitudeSignBlock64::from_codes(&zeros); + let n = MagnitudeSignBlock64::from_codes(&negzero); + assert_eq!(n.fused_dot(&z), Err(ProbeError::Nan)); + assert_eq!(z.fused_dot(&n), Err(ProbeError::Nan)); + } + + #[test] + fn overflow_is_not_misreported_as_nan() { + let plus = SignedMagnitude9::from_code(255); + let minus = SignedMagnitude9::from_code(511); + assert_eq!(plus.mul_add(plus, i32::MAX), Err(ProbeError::Overflow)); + assert_eq!(minus.mul_add(plus, i32::MIN), Err(ProbeError::Overflow)); + } +} From 7b2e27cf1ce43c62078cb0229832d6182bd6aa99 Mon Sep 17 00:00:00 2001 From: AdaWorldAPI Date: Tue, 27 Oct 2026 14:55:46 +0100 Subject: [PATCH 2/4] test(lab): make palette512 example runnable as standalone smoke probe --- examples/palette512_probe.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/palette512_probe.rs b/examples/palette512_probe.rs index ecf87e06..8668bea2 100644 --- a/examples/palette512_probe.rs +++ b/examples/palette512_probe.rs @@ -96,7 +96,21 @@ impl MagnitudeSignBlock64 { } fn main() { - println!("Palette512 SignedMagnitude9: run cargo test --example palette512_probe"); + let plus = SignedMagnitude9::from_code(0x0ff); + let minus = SignedMagnitude9::from_code(0x1ff); + let signed_product = plus.mul_add(minus, 0).expect("valid product"); + let lanes = [plus.code(); 64]; + let block = MagnitudeSignBlock64::from_codes(&lanes); + let dot = block.fused_dot(&block).expect("valid 64-lane dot"); + println!( + "Palette512 SM9: +{} * {} = {}, 64-lane dot = {}, block = {} B", + plus.value().expect("valid value"), + minus.value().expect("valid value"), + signed_product, + dot, + std::mem::size_of::() + ); + println!("Run cargo test --example palette512_probe for exhaustive evidence"); } #[cfg(test)] From 30b515d403aad6988f368b073c68d2c5b904f6e5 Mon Sep 17 00:00:00 2001 From: AdaWorldAPI Date: Tue, 27 Oct 2026 14:57:02 +0100 Subject: [PATCH 3/4] docs(lab): pin Palette512 sign-magnitude hypothesis and falsifiers --- ...2-signed-magnitude9-experiment-20261027.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 .claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md diff --git a/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md b/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md new file mode 100644 index 00000000..3d3b433e --- /dev/null +++ b/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md @@ -0,0 +1,69 @@ +# Palette512 / SignedMagnitude9: isolated arithmetic research gate + +> **2026-10-27 · EXPERIMENT / NOT ADMITTED** +> Owner for this probe: **ndarray numerical kernels**. This note and `examples/palette512_probe.rs` do **not** approve a production codec, a BGZ codebook expansion, an ONNX/TFLite compatibility claim, a Quack/DT64 change, or a wire migration. + +## Hypothesis + +Use a separate sign plane with 64 unsigned magnitudes, giving 512 encodings per lane: + +- `sign=0, magnitude=0`: valid +0. +- `sign=0, magnitude=1..255`: +1..+255. +- `sign=1, magnitude=1..255`: -1..-255. +- `sign=1, magnitude=0`: one NaN/invalid sentinel (negative zero). + +There are **511 numeric values** and one invalid codeword, versus 255 numeric values and one NaN codeword for the existing 8-bit `Sym + NaNAtMin` domain (`-127..127` plus `-128`). At equal physical range, the added levels *could* allow finer quantization; calibrated quality and end-to-end cost are unmeasured. + +A **64-lane** block stores **64 bytes of u8 magnitude + one u64 sign mask = 72 bytes**, or exactly 9 bits per lane. This is a packed block representation, **not** a 9-bit signed integer that native hardware can multiply as `i8`. Physical `repr(C)` footprint is *not* a persisted wire format. + +### Arithmetic seam + +For magnitudes `a,b` and signs `sa,sb`: + +``` +product = u8(a) * u8(b) // widened temporary for correctness +acc = (sa XOR sb) ? (acc - product) : (acc + product) +``` + +That is the proposed **sign-aware multiply-accumulate**: sign handling belongs inside the operation, not in a prior full-buffer decode. The scalar probe uses checked `i32` accumulation and fails closed on NaN or overflow, distinctly. SIMD fusion is a *hypothesis*, not an implemented vector kernel or performance result. + +**Important:** x86 VNNI `VPDPBUSD` accepts `u8 × i8`, not two arbitrary `u8` magnitudes. A legal lowering must prove a correction or masking identity against the oracle. No SIMD efficiency is presumed. + +## Semantic separation from current Palette256 + +**This is not a generic 512-centroid palette.** Existing `bgz-tensor` `WeightPalette` uses `u8` *archetype indices*, with 256 distinct codes and associated pair LUTs. The sign-magnitude encoding instead indexes a *structured signed numeric domain*. It cannot reinterpret arbitrary Palette256 identifiers by declaring half of their coordinates negative, and it cannot steal an independent presence bit without proving the original presence contract. + +An actual **512-archetype** codebook would need a 9-bit index and new pair-LUT addressing: a flat 512×512×u16 table is 512 KiB, compared with 128 KiB at 256²; a 512×512×u16 compose table adds another 512 KiB rather than the existing 64 KiB u8 table. Any sign-symmetric factorization must be individually proven for the relevant distance/compose algebra. The present PR adds **none** of these tables. + +Other independent invalid states (SQL NULL, missing, unknown, denied) cannot be conflated with the single NaN sentinel. Quack's predicate/null semantics remain its own contract. + +## First executable evidence (this PR) + +`cargo test --example palette512_probe` + +Five tests (not executed by this session): + +1. Enumerate all 512 codewords and prove round-trip, **511 numeric + 1 NaN**. +2. Enumerate all **262,144 ordered pairs** of codewords; compare sign-aware `mul_add` against an independently decoded signed multiplication plus a nonzero accumulator. +3. Check a 64-lane sign-plane block against an independent wide `i64` dot-product reference and assert its 72-byte in-memory footprint. +4. Verify NaN propagation even when the other operand is zero. +5. Distinguish overflow from NaN, including positive and negative accumulator boundaries. + +This is a reference-quality **scalar example**, not SIMD code, a production API, or a calibrated numerical comparison. All five tests are **NOT RUN** locally (Rust toolchain unavailable). No CI result is asserted. + +## Admission and falsification + +- **Correctness falsifier:** Any of the 512² pairs disagrees with independently decoded arithmetic; positive zero aliases negative-zero sentinel; NaN silently becomes zero; overflow is misreported. +- **Representation falsifier:** Existing source presence/null meaning is lost, or conversion cannot preserve the independently required missingness states. +- **Numerical falsifier:** A *held-out, fixed dynamic-range* calibration shows no meaningful improvement in endpoint saturation/tail error/task-level accuracy versus current `Codec8`. +- **Performance falsifier:** After fair end-to-end measurement, packing+sign correction outweighs quality gains or unpacks/widening dominate; evaluate AVX2, AVX-512/VNNI, NEON, WASM, scalar separately, without assuming each ISA offers the same fused instruction. +- **Algebra falsifier:** Any attempted folded sign-aware LUT violates the independently defined distance/compose relation, especially zero/NaN, negation, and multi-hop cases. + +**Next gate, separately approved:** implement SIMD lowering and benchmark sign-aware `mul_add` vs established `i8` and `u8 × i8` kernels; measure cycles/value, packing cost, throughput and errors separately. A later **independent** gate may investigate compressed/factored 512-value LUT laws if relevant. + +## Related authorities (read-only links) + +- [ndarray Codec8 plan](https://github.com/AdaWorldAPI/ndarray/blob/master/.claude/plans/quantized-codec8-v2.md), [#370](https://github.com/AdaWorldAPI/ndarray/pull/370): compile-time numeric codec; **unchanged**. +- [Existing VNNI GEMM](https://github.com/AdaWorldAPI/ndarray/blob/master/src/hpc/vnni_gemm.rs): `u8 × i8 → i32`; **not** a ready-made unsigned-unsigned signed-mask MAC. +- [BGZ palette](https://github.com/AdaWorldAPI/lance-graph/blob/main/crates/bgz-tensor/src/palette.rs), [attention LUT](https://github.com/AdaWorldAPI/lance-graph/blob/main/crates/bgz-tensor/src/attention.rs): **distinct** archetype-index contract. +- [D-NAM-PZL / EXO-152](https://linear.app/exo-red/issue/EXO-152/d-nam-capstone-normalized-native-algebra-onnxtflitemlir-dto-method), [EXO-50 coordination](https://linear.app/exo-red/issue/EXO-50/agent-message-bus-gpt-xgrok-ccsession-id): context only; no production permission implied. From 7bb0ada3e159f27b03f89febebccdf260e422e23 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 19:59:56 +0000 Subject: [PATCH 4/4] palette512 probe: rustfmt, and correct the note's stale claims rustfmt reflows three expressions (the red format/stable check). The note said the five tests were not run; they pass on rustc 1.99.0. It also said x86 has no unsigned x unsigned dot product; AVX-VNNI-INT8 VPDPBUUD, NEON UDOT and wasm extmul do (from documentation, not measured). And it records the rule settled in #381: NaN is -0, a cleared presence bit over a stored 0. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01XAqKMPh2Seh2WLccCksAdY --- ...12-signed-magnitude9-experiment-20261027.md | 8 ++++---- examples/palette512_probe.rs | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md b/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md index 3d3b433e..d81da065 100644 --- a/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md +++ b/.claude/knowledge/palette512-signed-magnitude9-experiment-20261027.md @@ -27,7 +27,7 @@ acc = (sa XOR sb) ? (acc - product) : (acc + product) That is the proposed **sign-aware multiply-accumulate**: sign handling belongs inside the operation, not in a prior full-buffer decode. The scalar probe uses checked `i32` accumulation and fails closed on NaN or overflow, distinctly. SIMD fusion is a *hypothesis*, not an implemented vector kernel or performance result. -**Important:** x86 VNNI `VPDPBUSD` accepts `u8 × i8`, not two arbitrary `u8` magnitudes. A legal lowering must prove a correction or masking identity against the oracle. No SIMD efficiency is presumed. +**Important:** x86 VNNI `VPDPBUSD` accepts `u8 × i8`, not two arbitrary `u8` magnitudes. Unsigned × unsigned exists elsewhere: AVX-VNNI-INT8 `VPDPBUUD`, NEON `UDOT`, wasm `extmul_*_u8x16`, or zero-extend plus `pmaddwd`. That list is from documentation, not measured. A lowering that splits lanes by `sign_a ^ sign_b` into two unsigned accumulators (`dot = pos - neg`) needs no correction identity. No SIMD efficiency is presumed. ## Semantic separation from current Palette256 @@ -35,13 +35,13 @@ That is the proposed **sign-aware multiply-accumulate**: sign handling belongs i An actual **512-archetype** codebook would need a 9-bit index and new pair-LUT addressing: a flat 512×512×u16 table is 512 KiB, compared with 128 KiB at 256²; a 512×512×u16 compose table adds another 512 KiB rather than the existing 64 KiB u8 table. Any sign-symmetric factorization must be individually proven for the relevant distance/compose algebra. The present PR adds **none** of these tables. -Other independent invalid states (SQL NULL, missing, unknown, denied) cannot be conflated with the single NaN sentinel. Quack's predicate/null semantics remain its own contract. +**NaN = `-0` = presence clear over a stored 0** (settled in #381). Where the column has a presence plane, NaN is a cleared presence bit with magnitude 0 stored under it, and "sign set, magnitude 0" is the same NaN written without the plane. All of −255..=+255 then stay values, and NaN never reaches a multiply: lanes are selected by `presence_a & presence_b & filter` first. A codec that needs *several* distinguishable invalid states (NULL, unknown, denied) still needs more than one codeword. Quack's predicate/null semantics remain its own contract. ## First executable evidence (this PR) `cargo test --example palette512_probe` -Five tests (not executed by this session): +Five tests: 1. Enumerate all 512 codewords and prove round-trip, **511 numeric + 1 NaN**. 2. Enumerate all **262,144 ordered pairs** of codewords; compare sign-aware `mul_add` against an independently decoded signed multiplication plus a nonzero accumulator. @@ -49,7 +49,7 @@ Five tests (not executed by this session): 4. Verify NaN propagation even when the other operand is zero. 5. Distinguish overflow from NaN, including positive and negative accumulator boundaries. -This is a reference-quality **scalar example**, not SIMD code, a production API, or a calibrated numerical comparison. All five tests are **NOT RUN** locally (Rust toolchain unavailable). No CI result is asserted. +This is a reference-quality **scalar example**, not SIMD code, a production API, or a calibrated numerical comparison. All five tests pass (`cargo test --example palette512_probe`, rustc 1.99.0), including the 262,144-pair check. ## Admission and falsification diff --git a/examples/palette512_probe.rs b/examples/palette512_probe.rs index 8668bea2..6359505f 100644 --- a/examples/palette512_probe.rs +++ b/examples/palette512_probe.rs @@ -41,9 +41,7 @@ impl SignedMagnitude9 { /// Handle sign in the accumulator without a signed-8-bit operand. fn mul_add(self, rhs: Self, acc: i32) -> Result { - if (self.negative && self.magnitude == 0) - || (rhs.negative && rhs.magnitude == 0) - { + if (self.negative && self.magnitude == 0) || (rhs.negative && rhs.magnitude == 0) { return Err(ProbeError::Nan); } let product = i32::from(self.magnitude) * i32::from(rhs.magnitude); @@ -127,7 +125,11 @@ mod tests { match lane.value() { Ok(v) => { assert!((-255..=255).contains(&v)); - let expected = if code < 256 { code as i16 } else { -((code & 255) as i16) }; + let expected = if code < 256 { + code as i16 + } else { + -((code & 255) as i16) + }; assert_eq!(v, expected); valid += 1; } @@ -162,10 +164,10 @@ mod tests { fn sixty_four_lane_sign_plane_matches_wide_reference() { assert_eq!(std::mem::size_of::(), 72); let lhs = std::array::from_fn(|i| match i % 4 { - 0 => 0x0FF, // +255 - 1 => 0x1FF, // -255 - 2 => 0x000, // +0 - _ => 0x100 | i as u16, // negative magnitudes 3..63 + 0 => 0x0FF, // +255 + 1 => 0x1FF, // -255 + 2 => 0x000, // +0 + _ => 0x100 | i as u16, // negative magnitudes 3..63 }); let rhs = std::array::from_fn(|i| match i % 3 { 0 => 0x1FF,