diff --git a/.claude/board/ISSUES.md b/.claude/board/ISSUES.md index 52e435a1a..a545eccd0 100644 --- a/.claude/board/ISSUES.md +++ b/.claude/board/ISSUES.md @@ -1,5 +1,54 @@ # Issues Log — Open + Resolved (double-entry, append-only) +## ISS-PERTURBATION-P64-ADDRESS-IDENTITY-UNPROVEN (2026-08-26) — OPEN + +The dense perturbation field (`PerturbationDto.energy`, canonically 4096 +codebook-indexed f32) and the p64 palette field (64×64 = 4096 cells, addressed +`S/4 × O/4` from `CausalEdge64` via `edge_to_block`) were DESIGNED as one +space — the 64×64 field as the COCA codebook LUT, SPO 2³ as its 8× +amortization, CE64's 3×8 = 24 SPO bits carrying the NARS decomposition +(design intent, operator 2026-08-26). But intent is not identity: the code +has never proven `energy[i] ↔ cell[i/64][i%64] ↔ (S/4, O/4)`, and the two +spaces may have drifted apart across the format history. The audit question (Q1), +before any wiring: what was the canonical `codebook_id[0..4095] ↔ +(row, col)[0..63]²` map — (A) row-major `id>>6, id&63`, (B) Morton +deinterleave 12→6+6, (C) codebook-specific permutation? No mapping exists in +code today (grep: thinking-engine, p64-bridge, deepnsm — zero hits for the +6+6 split); historical P64/COCA/Morton docs and fixtures are the evidence +base. A row-major cast tests green under bijection even if the truth was +Morton — and still spatially scrambles the field. Until `energy[i] ↔ cell[row][col]` +is proven, no lowering of the perturbation field onto the p64 mask ALU may be +wired — count-equality is not identity, and wiring on it would be +representation-before-generator (grounding-descent plan §7a's named error). + +Probe design (CONTROL: today's top_k→min/max window as baseline; EXPERIMENT: +energy → proven 64×64 mask → p64 combine/contra/style; SABOTAGE: permute the +energy↔cell addressing — same result under permutation kills the ALU +hypothesis) recorded in +`E-THE-PERTURBATION-FIELD-NEVER-REACHED-THE-MASK-ALU-1`. + +Scope sharpened same day (operator): the FORWARD design question is not to +restore the 4096-lexicon mapping — the idea migrated up into the token seam +(tesseract-paperless → #1017 token receipt → DeepNSM-v2 on the 256:256 rail, +#798). p64's modern role is a WORKING-SET ALU: the question is where in that +intake path a ≤ 4096 active set first arises to justify the 64×64 mask field. +Q1 stays open as history forensics (it decides whether any legacy fixture can +be read spatially at all). + +Second gate, sequenced strictly behind Q1 (added same day): the METRIC +hypothesis — helix24/Fisher-2Z as the field's distance (2Z = geodesic ρ, the +exactly-uniform LUT axis per #1040). It must not appear to solve Q1: cell +identity first, distance-between-cells after. Decisive design: same IDs / +energies / masks / Top-K budget, A = Morton+2Z, B = Morton+grid, C = +Morton+permuted-2Z; `A ≈ C` kills the metric, `A > B ∧ A > C` confirms it. +Gate (a) for the energy register is MEASURED (PROBE-ENERGY-ARCTANH-DOMAIN): +domain holds for the field, fails exactly at the attractor (max == 1.0 is the +fixed point) — winner excluded or clamped at documented depth (2Z ceiling +14.5/21.4 ρ for ε = 1e-6/1e-9). + +Not actioned: the probe needs real (non-induced) perturbation fields, and the +mapping proof is a measurement, not an inspection. + ## ISS-GATEDECISION-ORDINAL-COLLISION (2026-08-26) — OPEN **Two live types are named `GateDecision`, and their locked byte mappings are diff --git a/.claude/board/entries/2026-08-26-e-the-perturbation-field-never-reached-the-mask-alu-1.md b/.claude/board/entries/2026-08-26-e-the-perturbation-field-never-reached-the-mask-alu-1.md new file mode 100644 index 000000000..b4454c685 --- /dev/null +++ b/.claude/board/entries/2026-08-26-e-the-perturbation-field-never-reached-the-mask-alu-1.md @@ -0,0 +1,175 @@ +### E-THE-PERTURBATION-FIELD-NEVER-REACHED-THE-MASK-ALU-1 + +**Audited 2026-08-26** (session review of the p64 ⟷ cognitive-shader-driver +seam; operator analysis ratified against source, every claim file:line). + +**The finding.** `StreamDto` / `PerturbationDto` / `BusDto` do NOT form an ALU +chain between p64 and the driver — they form an **adapter/transport seam**, +and the dense field is dropped at it: + +``` +StreamDto.codebook_indices → ingest_codebook_indices → BindSpace (transport) +PerturbationDto.energy: Vec ← stays behind, UNCONSUMED +PerturbationDto.top_k → dispatch_from_top_k + → filter e > SCAN_WORTHY_ENERGY + → min(idx)..=max(idx) → ColumnWindow (window!) +ShaderBus → BusDto → dispatch_busdto → Binary16K + qualia + meta (projection) +``` + +`{7, 42, 900}` becomes *scan rows 7..901*, not the mask `{7, 42, 900}` — a +control-plane window heuristic, not mask-native execution. Meanwhile p64 IS +the mask surface (`edge_to_layer_mask` → `[u64;64]` / `[[u64;64];8]`, +`StyleParams{layer_mask, combine, contra, density_target}`) and is **DTO-blind**: +none of the three names appears in `p64-bridge`. + +**The 4096 ≟ 4096 question — design intent recovered (operator, 2026-08-26).** +The two spaces were DESIGNED as one: the 64×64 field was meant as the COCA +codebook LUT (4096 vocabulary), with the SPO 2³ decomposition as its 8× +amortization and meta-awareness carried on `CausalEdge64` through the NARS +decomposition — which is WHY CE64 spends 3×8 = 24 bits on S/P/O. The p64 +address `S/4 × O/4` (`edge_to_block`: 256×256 SO space → 64×64 blocks of 4) +was intended to land on the same 4096 places the codebook indexes. + +That upgrades the question, it does not answer it: intent is not identity, and +the CODE has never proven `energy[i] ↔ cell[i/64][i%64] ↔ (S/4, O/4)`. Two +4096-place spaces designed as one can still have drifted into different +universes across the format history. Wiring on the count (or on the recovered +intent) alone would be representation-before-generator (7a's named error). +The mapping must be PROVEN — and the intent now supplies the concrete +hypothesis the probe tests: + +``` +CONTROL top_k → min/max ColumnWindow (today's arm, the baseline) +EXPERIMENT energy field → proven 64×64 mask → p64 ALU → combine/contra/style +SABOTAGE permute the energy↔cell addressing +``` + +If the sabotage permutation yields the same result, the spatial assignment +carries no information and the ALU hypothesis dies. Gate: +`ISS-PERTURBATION-P64-ADDRESS-IDENTITY-UNPROVEN`. + +**Why this matters beyond hygiene:** this seam is very plausibly the piece of +the recovered resonance architecture (grounding-descent plan §7h) that existed +on paper and never made the last metre to the mask ALU — the dense +resonance field exists, the mask ALU exists, and the join between them +collapsed to top-k → window during implementation. + +**Two code defects fixed alongside (same PR):** +1. `dto.rs` claimed `PerturbationDto IS f64[4096]` twice — the field is + `Vec`, wrong scalar AND wrong shape, and `from_energy_f32` accepts + arbitrary slice lengths unchecked. Docs corrected; behaviour untouched. +2. `busdto_to_binary16k` silently aliased any index ≥ 16384 onto a foreign + bit via `% width_bits`. A hard `assert!` was tried FIRST and refuted by + the existing corner-corpus test: `u16::MAX` is a pinned legal transport + value (the headline round-trips losslessly via `qualia[9]`, never via the + plane). Correct semantics: **skip, never alias** — an out-of-plane index + sets NO bit, a documented loss in the same class as supporters with + energy ≤ `SUPPORT_ENERGY` (likewise unrecoverable from the plane). + Anti-aliasing + plane-edge tests added; before the fix, recovery reported + a phantom top_k index `1` no producer ever emitted. The same wrap in + `ingest_codebook_indices` is DOCUMENTED, not changed (lab-only + gRPC/serve surface). + +**The lost address contract, reconstructed (operator, same session).** The +4096 equality is not coincidence but a broken wire: the intended stack was + +``` +COCA codebook, 4096 atoms → 12-bit address → 6 bit | 6 bit → 64×64 LUT + → superposition/perturbation over the field + → NARS 2³ decomposition (8 relational READINGS of the same field — + the 8× amortization: same address, different reading; proto-ClassView) + → S/P/O result → CausalEdge64 bits 0..23 (3×8 SPO — the fossil that + survives verbatim in the v2 layout) → meta-awareness / MUL +``` + +Today's `edge_to_block` (`S/4 × O/4` over the 256×256 SO space) is plausibly +a LATER re-use of the same 64×64 geometry, not the original codebook +addressing. `PerturbationDto` itself is the tell: `energy` sized by +`CODEBOOK_SIZE = 4096` plus `top_k: [_; 8]` — though whether the 8 echoes +the 2³ decomposition or is merely "top eight" is NOT proven and needs +historical code/docs. Lineage note: the COCA-as-tokens usage entered around +the deepnsm-v2 → paperless-rs / tesseract-rs arc (~PR −10..−25 from here). + +The audit question for the HISTORY (now in the ISSUES entry): what was the +canonical map `codebook_id[0..4095] ↔ (row, col)[0..63]²` — +(A) row-major `id>>6, id&63`, (B) Morton deinterleave 12→6+6, or (C) a +codebook-specific permutation? A row-major cast would be bijective and test +green even if the truth was Morton — and still spatially scramble the field. +A grep for `>>6`/`&63`/Morton in thinking-engine, p64-bridge, and deepnsm +finds NO mapping in code today: the wire is fully torn, both ends live. + +**Where the idea actually went (operator, same session): UP, into the +intake/token layer — not lost, migrated.** The old path +(COCA 4096 → 64×64 → SPO/NARS 2³ → CE64 → shader) has a modern successor: + +``` +PDF/image → tesseract-rs (crates/tesseract-paperless) → DocIr regions + → ONE versioned tokenization receipt (#1017: one span tokenization serves + Tantivy + DeepNSM-v2 + forward-prediction, no per-consumer retokenize) + → COCA / academic lexical identity (18,559 real surface forms; DeepNSM-v2 + leaves the last basins deliberately empty rather than padding) + → DeepNSM-v2 WordId + Pos on the 256:256 token rail (#798 broke the 4096 + ceiling: 16-bit vocabulary, 65,536 addresses — Alice used 7,675) + → SPO → NARS/causal → CausalEdge64 / witness / alpha +``` + +So 4096 was never "the semantic population" — it was the first workable +working surface. The forward question is therefore NOT "wire the old DTOs +into p64" and NOT "squeeze 18k/65k back into 4096". p64's modern role is a +**WORKING SET / local ALU surface**: attention/current span/current basin +selects ≤ 4096 active relations, and THOSE live on the 64×64 mask field — +"4096 things addressable simultaneously in this local resonance field", not +"4096 concepts exist". The open design question, sharper than the wiring one: +**where between `tesseract-paperless → token receipt → DeepNSM-v2` does a +small-enough active set first arise to justify a 64×64 mask-native ALU?** + + +**Addendum, same session — can helix24 (Fisher-2Z LUT) carry the perturbation +energy? Gated YES (idea, unprobed).** The alignment is threefold and each leg +is already measured elsewhere today: + +1. **The energy values are similarity-shaped.** `PerturbationDto.energy` is + produced as `next[j] += table[i][j] · energy[i]` over the 4096×4096 u8 + distance table (`thinking-engine/src/engine.rs`, scale `1/(255−floor)`) — + correlation-derived, non-negative. Fisher z is the variance-stabilising + axis for exactly this shape, and + `E-THE-FOUR-READINGS-OF-ONE-HELIX-CARRIER-…-1` measured 2Z as EXACTLY + uniform as a LUT-over-field axis (r distorts 204×, y 11.6×) — and the + perturbation field IS a LUT over a field. +2. **helix24's weakness is irrelevant here, its strength is the point.** + The pole-penalty entry measured helix24 (`ResidueEdge`, 3 B) as bounded + (∝ y) where helix48's polar byte diverges (∝ 1/r), winning on INDEX and + losing on ANGLE. Perturbation energy needs index/magnitude, never angle — + the carrier's losing axis is unused. +3. **The geometry already meets in the middle.** The engine's own compute + plan ("Option B: Block Tiling / Rollrasen") tiles the 4096×4096 table + into **64×64 blocks** with the energy chunk register-resident — the p64 + working-field geometry is already the engine's tiling unit. A 64×64 + working set with per-cell helix24 energy is 12 KB (vs 16 KB f32), in the + units (2Z = geodesic ρ) that compose across families — and additive + thresholds in z-space make the SCAN_WORTHY/SUPPORT pair scale-free. + +Gates, in order: (a) the DOMAIN proof — accumulated energy is not bounded to +the arctanh domain a priori; the normalisation (converged fixed point? per-cycle +renorm?) must be measured before any `atanh` touches it (`Similarity::CLAMP_EPS` +exists, but clamping is a bandage, not a domain proof); (b) sequenced BEHIND +`ISS-PERTURBATION-P64-ADDRESS-IDENTITY-UNPROVEN` — an energy register on a +field whose addressing is unproven inherits the scramble; (c) zero-copy law — +helix24 as THE carrier of the working-set cell or as a transport codec, never +a second store beside the f32. + +The intake seam already practices the same derive-at-the-address doctrine +this arc keeps confirming: DocIr has document identity (no minted source_id), +Region has span identity (no span_id), the token receipt exists (no +per-consumer retokenize), COCA/DeepNSM give lexical identity (no second text +world). The DTO path audited above reads, in hindsight, like a temporary +bypass built while that token/resident-state infrastructure did not yet +exist. +3. The two energy thresholds are now NAMED (`SCAN_WORTHY_ENERGY = 0.01`, + `SUPPORT_ENERGY = 0.0`) with cross-referencing docs: worth-scanning is + deliberately stricter than worth-recording-as-support. Distinct roles, + not drift — the comment is the receipt. + +Receipts: `engine_bridge.rs` (seam + fixes), `thinking-engine/src/dto.rs`, +`p64-bridge/src/lib.rs` (`edge_to_block` S/4×O/4), `busdto_bridge_test.rs` +(plane contract + new falsifier legs). diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index f07ff077f..29d259a15 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -28,6 +28,7 @@ up in exactly one of them, never both. | date | entry id | finding | file | |---|---|---|---| +| 2026-08-26 | `E-THE-PERTURBATION-FIELD-NEVER-REACHED-THE-MASK-ALU-1` | the three DTOs are an adapter seam, not an ALU chain: PerturbationDto.energy is dropped, top_k collapses to a min/max window, and the p64 mask ALU is DTO-blind; 4096==4096 is not an address identity (S/4×O/4 vs codebook) — probe gate filed | [2026-08-26-e-the-perturbation-field-never-reached-the-mask-alu-1.md](2026-08-26-e-the-perturbation-field-never-reached-the-mask-alu-1.md) | | 2026-08-26 | `E-STYLES-ANCHOR-AT-RUNG-4-IS-A-SCALAR-ERA-ARTIFACT-EVERY-INSTANTIATED-STRATUM-CARRIES-A-STYLE-1` | styles-at-rung-4 was the scalar-rung era's address, not the type: under the tower every stratum selects its style from problem-texture resonance; its ΔF is the outcome channel that reinforces, revises, or reopens the selection | [2026-08-26-e-styles-anchor-at-rung-4-is-a-scalar-era-artifact-every-instantiated-stratum-carries-a-style-1.md](2026-08-26-e-styles-anchor-at-rung-4-is-a-scalar-era-artifact-every-instantiated-stratum-carries-a-style-1.md) | | 2026-08-26 | `E-THE-HELIX-POLE-PENALTY-IS-THE-POLAR-BYTE-NOT-THE-CODEC-AND-THE-SPRITE-DECODE-NEVER-TOUCHES-THE-GEOMETRY-1` | measured: BOTH carriers degrade toward the pole, but helix24's term is BOUNDED (∝ y) while helix48's polar byt… | [2026-08-26-e-the-helix-pole-penalty-is-the-polar-byte-not-the-codec-and-the-sprite-decode-never-touches-the-geometry-1.md](2026-08-26-e-the-helix-pole-penalty-is-the-polar-byte-not-the-codec-and-the-sprite-decode-never-touches-the-geometry-1.md) | | 2026-08-26 | `E-THE-FOUR-READINGS-OF-ONE-HELIX-CARRIER-AND-WHY-2Z-IS-CANONICAL-FOR-LUT-OVER-FIELD-1` | measured: for a LUT over a field, `r` distorts a splat kernel 204×, `y` 11.6×, and 1Z/2Z are both EXACTLY unif… | [2026-08-26-e-the-four-readings-of-one-helix-carrier-and-why-2z-is-canonical-for-lut-over-field-1.md](2026-08-26-e-the-four-readings-of-one-helix-carrier-and-why-2z-is-canonical-for-lut-over-field-1.md) | diff --git a/crates/cognitive-shader-driver/src/engine_bridge.rs b/crates/cognitive-shader-driver/src/engine_bridge.rs index e141c67c5..b92c6631b 100644 --- a/crates/cognitive-shader-driver/src/engine_bridge.rs +++ b/crates/cognitive-shader-driver/src/engine_bridge.rs @@ -71,6 +71,13 @@ pub fn ingest_codebook_indices( } // Build content fingerprint: set bit at `idx` position. + // + // The modulo wrap below shares the aliasing defect fixed in + // `busdto_to_binary16k` (an index >= 16384 lands on a foreign bit), + // but this arm is fed by the LAB-ONLY gRPC/serve surface with + // caller-supplied indices — a hard assert here would let a network + // client panic the lab server. Documented instead of asserted; + // canonical inputs are codebook indices < 4096 and never wrap. let mut content = [0u64; WORDS_PER_FP]; let bit = idx as usize % (WORDS_PER_FP * 64); content[bit / 64] |= 1u64 << (bit % 64); @@ -91,8 +98,26 @@ pub fn ingest_codebook_indices( // PerturbationDto → ShaderDispatch (top-k seeds the scan window) // ═══════════════════════════════════════════════════════════════════════════ +/// Energy above which a top-k index is *worth scanning* — the control-plane +/// admission threshold of [`dispatch_from_top_k`]. Deliberately STRICTER than +/// [`SUPPORT_ENERGY`]: scanning costs cycles, recording support costs a bit. +/// The two thresholds are distinct on purpose; a change to either must say +/// which role it means (they are not accidental drift — this comment is the +/// receipt). +pub const SCAN_WORTHY_ENERGY: f32 = 0.01; + /// Build a ShaderDispatch from resonance top-k. /// +/// **This is a control-plane WINDOW heuristic, not a mask.** The surviving +/// indices collapse to `min..=max` — `{7, 42, 900}` scans rows `7..901`, not +/// the set `{7, 42, 900}`. The dense `PerturbationDto::energy` field never +/// reaches this seam at all; only `top_k` does. See the board entry +/// `E-THE-PERTURBATION-FIELD-NEVER-REACHED-THE-MASK-ALU-1` and +/// `ISS-PERTURBATION-P64-ADDRESS-IDENTITY-UNPROVEN` before "fixing" this into +/// a mask: the address identity between `energy[i]` and the p64 64×64 cell +/// (`S/4 × O/4`) is UNPROVEN, and lowering without that proof is +/// representation-before-generator. +/// /// The top-k codebook indices from the thinking-engine's resonance field /// become the row window for the shader to scan. If the BindSpace has /// been populated via `ingest_codebook_indices`, the rows correspond @@ -104,7 +129,7 @@ pub fn dispatch_from_top_k( ) -> ShaderDispatch { let active: Vec = top_k .iter() - .filter(|&&(_, e)| e > 0.01) + .filter(|&&(_, e)| e > SCAN_WORTHY_ENERGY) .map(|&(idx, _)| idx) .collect(); @@ -200,22 +225,39 @@ const NARS_F_FROM_INDEX_LOW: bool = true; #[cfg(feature = "with-engine")] const TOP_K_ENERGY_BASE_DIM: usize = 1; // qualia[0] = headline energy, [1..9] = top_k energies +/// Energy above which a top-k entry is *worth recording as support* — the +/// commit-bit threshold of [`busdto_to_binary16k`]. Deliberately LOOSER than +/// [`SCAN_WORTHY_ENERGY`]: see that constant's doc for why the two differ. +#[cfg(feature = "with-engine")] +pub const SUPPORT_ENERGY: f32 = 0.0; + /// Encode a BusDto's codebook_index + top_k indices as a `[u64; WORDS_PER_FP]` -/// Binary16K accumulator. Each index sets one bit at `idx % WIDTH_BITS`. -/// `top_k` entries with energy ≤ 0.0 are skipped (zero-energy = no support). +/// Binary16K accumulator. Each in-plane index sets one bit; out-of-plane +/// indices set none (see the closure comment). `top_k` entries with energy +/// ≤ [`SUPPORT_ENERGY`] are skipped (no support recorded). #[cfg(feature = "with-engine")] fn busdto_to_binary16k(bus: &BusDto) -> [u64; WORDS_PER_FP] { let width_bits = WORDS_PER_FP * 64; let mut bits = [0u64; WORDS_PER_FP]; let mut set_bit = |idx: u16| { - let pos = (idx as usize) % width_bits; - bits[pos / 64] |= 1u64 << (pos % 64); + // Out-of-plane indices set NO bit — never a foreign one. The modulo + // this replaces aliased any idx >= 16384 onto an unrelated bit (a + // plausible wrong answer). A hard assert is ALSO wrong here: the + // corner-corpus roundtrip test pins u16::MAX as a legal transport + // value (the headline survives losslessly via qualia[9], not via + // this plane), so out-of-plane is a DOCUMENTED loss — the same + // class as supporters with energy <= SUPPORT_ENERGY, whose indices + // are likewise unrecoverable from the plane. Skip, don't lie. + let pos = idx as usize; + if pos < width_bits { + bits[pos / 64] |= 1u64 << (pos % 64); + } }; // Headline: codebook_index always sets a bit (BusDto IS a committed thought). set_bit(bus.codebook_index); - // Top-K supporters: only those with positive energy contribute a bit. + // Top-K supporters: only those above SUPPORT_ENERGY contribute a bit. for &(idx, e) in bus.top_k.iter() { - if e > 0.0 { + if e > SUPPORT_ENERGY { set_bit(idx); } } diff --git a/crates/cognitive-shader-driver/tests/busdto_bridge_test.rs b/crates/cognitive-shader-driver/tests/busdto_bridge_test.rs index a37038caa..50e5a6130 100644 --- a/crates/cognitive-shader-driver/tests/busdto_bridge_test.rs +++ b/crates/cognitive-shader-driver/tests/busdto_bridge_test.rs @@ -317,3 +317,59 @@ fn busdto_codebook_index_corner_corpus_round_trips_bit_exact() { ); } } + +/// Anti-aliasing leg: an out-of-plane supporter index (16385 — under the old +/// modulo this aliased onto bit 1) must produce NO phantom bit. Before the +/// fix, recovery reported a top_k index of 1 that no producer ever emitted. +/// (A hard assert was tried first and refuted by the corner-corpus test: +/// u16::MAX is a pinned legal transport value, lossless via qualia[9].) +#[test] +fn busdto_out_of_plane_index_sets_no_foreign_bit() { + let mut bs = BindSpace::zeros(2); + let bus = BusDto { + codebook_index: 4321, + energy: 0.5, + top_k: [ + (4321, 0.5), + (16385, 0.4), // out-of-plane: old code aliased this onto bit 1 + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + ], + cycle_count: 1, + converged: true, + }; + dispatch_busdto(&mut bs, 0, &bus, 0); + let recovered = unbind_busdto(&bs, 0); + assert!( + !recovered.top_k.iter().any(|&(idx, _)| idx == 1), + "phantom bit 1 recovered — out-of-plane index was aliased: {:?}", + recovered.top_k + ); +} + +/// Can-stay-silent leg: the highest VALID position (16383) commits cleanly. +#[test] +fn busdto_index_at_plane_edge_is_valid() { + let mut bs = BindSpace::zeros(2); + let bus = BusDto { + codebook_index: 16383, + energy: 0.5, + top_k: [ + (16383, 0.5), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + (0, -1.0), + ], + cycle_count: 1, + converged: true, + }; + let _ = dispatch_busdto(&mut bs, 0, &bus, 0); +} diff --git a/crates/thinking-engine/Cargo.lock b/crates/thinking-engine/Cargo.lock index 32ff397ed..43f9edec9 100644 --- a/crates/thinking-engine/Cargo.lock +++ b/crates/thinking-engine/Cargo.lock @@ -47,23 +47,11 @@ dependencies = [ "libc", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "arrow-array" -version = "58.3.0" +version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd33d3e92f207444098c75b42de99d329562be0cf686b307b097cc52b4e999e" +checksum = "ae33dad492b7df00a217563a7b0ef2874df68a0deea1b1a3acf628152f7f7a69" dependencies = [ "ahash", "arrow-buffer", @@ -79,9 +67,9 @@ dependencies = [ [[package]] name = "arrow-buffer" -version = "58.3.0" +version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6cd424c2693bcdbc150d843dc9d4d137dd2de4782ce6df491ad11a3a0416c0" +checksum = "b9552f96391c005e6ab449fa941420935e7e062489b12b8b1b08879b2163f5b5" dependencies = [ "bytes", "half", @@ -91,9 +79,9 @@ dependencies = [ [[package]] name = "arrow-data" -version = "58.3.0" +version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c88210023a2bfee1896af366309a3028fc3bcbd6515fa29a7990ee1baa08ee0" +checksum = "2b24852db04738907e06c04ea61e42fe7fda962a34513022dc0d0e754fb7976b" dependencies = [ "arrow-buffer", "arrow-schema", @@ -104,9 +92,9 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "58.3.0" +version = "58.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f633dbfdf39c039ada1bf9e34c694816eb71fbb7dc78f613993b7245e078a1ed" +checksum = "21ca356ad6425cecb6eb7b28e4f659f1ee7880fbb1a16127de7dd62901efee9e" [[package]] name = "atomic-waker" @@ -174,20 +162,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -[[package]] -name = "blake3" -version = "1.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "cpufeatures", -] - [[package]] name = "bumpalo" version = "3.20.2" @@ -384,12 +358,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "constant_time_eq" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" - [[package]] name = "core-foundation" version = "0.9.4" @@ -416,15 +384,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpufeatures" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -1572,7 +1531,6 @@ dependencies = [ name = "ndarray" version = "0.17.2" dependencies = [ - "blake3", "fractal", "matrixmultiply", "num-complex", diff --git a/crates/thinking-engine/examples/probe_energy_arctanh_domain.rs b/crates/thinking-engine/examples/probe_energy_arctanh_domain.rs new file mode 100644 index 000000000..23073c864 --- /dev/null +++ b/crates/thinking-engine/examples/probe_energy_arctanh_domain.rs @@ -0,0 +1,97 @@ +//! PROBE-ENERGY-ARCTANH-DOMAIN — the gate (a) measurement for the helix24 +//! energy-register idea (board: E-THE-PERTURBATION-FIELD-NEVER-REACHED-THE- +//! MASK-ALU-1, addendum). +//! +//! Question: is post-cycle `ThinkingEngine::energy` inside the arctanh +//! domain, and is the boundary reachable? The analytical half: `cycle()` +//! sum-normalises (`total energy = 1.0`), so every cell is in [0, 1] with +//! Σ = 1. The dangerous value is exactly 1.0 — `atanh(1) = ∞` — and a +//! delta distribution is not an edge case here, it is the ATTRACTOR the +//! engine converges toward. This probe measures the real code: +//! +//! P1 invariant: max ≤ 1.0 and Σ ≈ 1 across random tables and cycles +//! P2 the attractor: a table funnelling all mass into one column must +//! reach max == 1.0 EXACTLY (f32), i.e. atanh = +inf — can-fire leg +//! P3 a diffuse table stays strictly < 1.0 — can-stay-silent leg +//! P4 saturation depth: with Similarity::CLAMP_EPS-style clamping, +//! 2Z(1−ε) = ln(2/ε − 1) ≈ the finite ceiling the register carries +//! +//! Run: cargo run --manifest-path crates/thinking-engine/Cargo.toml \ +//! --example probe_energy_arctanh_domain + +use thinking_engine::engine::ThinkingEngine; + +// SplitMix64 (Rule 23 discipline: deterministic seed, no wall clock). +struct Rng(u64); +impl Rng { + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9E3779B97F4A7C15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); + z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB); + z ^ (z >> 31) + } +} + +fn stats(e: &[f32]) -> (f32, f32, usize) { + let max = e.iter().cloned().fold(0.0f32, f32::max); + let sum: f32 = e.iter().sum(); + let inf = e.iter().filter(|&&x| x.atanh().is_infinite()).count(); + (max, sum, inf) +} + +fn main() { + const N: usize = 256; // small field, same math as 4096 + + // ── P1 + P3: random diffuse tables ──────────────────────────────── + let mut rng = Rng(0x5EED); + let mut worst_max = 0.0f32; + let mut worst_sum_dev = 0.0f32; + for trial in 0..8 { + let table: Vec = (0..N * N).map(|_| (rng.next() % 256) as u8).collect(); + let mut eng = ThinkingEngine::new(table); + // seed: a handful of active atoms + for k in 0..4 { + eng.energy[(trial * 7 + k * 61) % N] = 0.25; + } + for _ in 0..32 { + eng.cycle(); + let (max, sum, inf) = stats(&eng.energy); + worst_max = worst_max.max(max); + worst_sum_dev = worst_sum_dev.max((sum - 1.0).abs()); + assert_eq!(inf, 0, "P3 FAILED: diffuse table produced atanh=inf"); + assert!(max <= 1.0, "P1 FAILED: max {max} > 1.0"); + } + } + println!("P1 invariant held over 8 tables x 32 cycles: max<=1.0, |sum-1| <= {worst_sum_dev:.2e}"); + println!("P3 diffuse tables: worst max = {worst_max:.6} (< 1.0, atanh finite everywhere)"); + + // ── P2: the attractor — all similarity funnels into column 0 ────── + // row[i][0] = 255, everything else 0 ⇒ after one cycle all mass is in + // cell 0 and normalisation makes it EXACTLY 1.0. + let mut table = vec![0u8; N * N]; + for i in 0..N { + table[i * N] = 255; + } + let mut eng = ThinkingEngine::new(table); + eng.energy[3] = 0.5; + eng.energy[100] = 0.5; + eng.cycle(); + let (max, _sum, inf) = stats(&eng.energy); + println!("P2 attractor: max = {max:?} (bits {:#010X}), atanh-inf cells = {inf}", max.to_bits()); + assert_eq!(max, 1.0, "P2: attractor must reach exactly 1.0"); + assert_eq!(inf, 1, "P2: exactly the winner cell must be atanh-infinite"); + + // ── P4: saturation depth under clamp ────────────────────────────── + for eps in [1e-6f64, 1e-9f64] { + let two_z = ((2.0 - eps) / eps).ln(); // 2·atanh(1-ε) = ln((2-ε)/ε) + println!("P4 clamp 1-{eps:.0e}: 2Z ceiling = {two_z:.3} rho (8-bit bin over [0,ceiling] = {:.4} rho)", two_z / 255.0); + } + + println!("\nVERDICT: domain proof HOLDS for the field, FAILS at the attractor —"); + println!("energy==1.0 is the engine's fixed point, not an outlier. A helix24"); + println!("energy register must either EXCLUDE the committed winner (it is the"); + println!("BusDto headline, carried losslessly elsewhere) or clamp with the"); + println!("documented, finite saturation depth above. Clamping without saying"); + println!("so would silently place every converged thought at the same depth."); +} diff --git a/crates/thinking-engine/src/dto.rs b/crates/thinking-engine/src/dto.rs index 795d13b94..4d4c47b03 100644 --- a/crates/thinking-engine/src/dto.rs +++ b/crates/thinking-engine/src/dto.rs @@ -1,7 +1,7 @@ //! DTOs: bus adapters between cognitive speed zones. //! //! Φ Dispersion: StreamDto — sensor output enters the field -//! Ψ Interference: PerturbationDto — the ripple field IS f64[4096] +//! Ψ Interference: PerturbationDto — the ripple field: f32 energy, canonically CODEBOOK_SIZE = 4096 //! B Consequence: BusDto — committed thought with provenance //! Γ Collapse: ThoughtStruct — stabilized, persisted, text is lazy @@ -50,7 +50,9 @@ pub struct StreamDto { // Ψ — PerturbationDto: the ripple field // ═══════════════════════════════════════════════════════════════════════════ -/// PerturbationDto IS f64[4096] energy. Not a struct with candidate lists. +/// PerturbationDto carries `Vec` energy (canonically CODEBOOK_SIZE = 4096 +/// entries — the doc previously claimed `f64[4096]`, wrong on both scalar type +/// and shape; `from_energy_f32` accepts arbitrary slice lengths, unchecked). /// /// High energy at entry 42 = "thought 42 resonates." /// Zero at entry 200 = "thought 200 destructively interfered."