|
| 1 | +//! Entropy-ladder probe — Staunen↔Wisdom over a synthetic NARS edge population. |
| 2 | +//! |
| 3 | +//! Shows the ladder coordinate doing real work: |
| 4 | +//! * the syntax/semantics/pragmatics rung partition, |
| 5 | +//! * the entropy×energy quadrant partition (Staunen/Confusion/Boredom/Wisdom), |
| 6 | +//! * the validation number — Spearman ρ(entropy, empirical accuracy) — proving |
| 7 | +//! entropy is a reliability proxy (more negative = stronger), and |
| 8 | +//! * Pearl-2³ SPO decomposition over the 3×palette-256 indices. |
| 9 | +//! |
| 10 | +//! cargo run --release --example entropy_ladder_probe --features std |
| 11 | +
|
| 12 | +use ndarray::hpc::entropy_ladder::{decompose_spo, entropy_class, nars_entropy, EntropyRung, Quadrant}; |
| 13 | +use ndarray::hpc::reliability::spearman; |
| 14 | + |
| 15 | +fn splitmix(s: &mut u64) -> f64 { |
| 16 | + *s = s.wrapping_add(0x9E37_79B9_7F4A_7C15); |
| 17 | + let mut z = *s; |
| 18 | + z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); |
| 19 | + z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); |
| 20 | + z ^= z >> 31; |
| 21 | + (z >> 11) as f64 / (1u64 << 53) as f64 |
| 22 | +} |
| 23 | + |
| 24 | +fn main() { |
| 25 | + println!("== Entropy ladder: Staunen↔Wisdom over a synthetic NARS edge population ==\n"); |
| 26 | + |
| 27 | + let mut s = 0x5EED_1ADD_u64.wrapping_add(0xA11CE); |
| 28 | + let n = 20_000usize; |
| 29 | + let max_obs = 48u32; |
| 30 | + |
| 31 | + let mut entropies = Vec::with_capacity(n); |
| 32 | + let mut accuracies = Vec::with_capacity(n); |
| 33 | + let mut rungs = [0usize; 3]; // Syntax, Semantics, Pragmatics |
| 34 | + let mut quads = [0usize; 4]; // Staunen, Confusion, Boredom, Wisdom |
| 35 | + let mut classes = [0usize; 4]; // 3-spare-bit reliability classes |
| 36 | + |
| 37 | + for _ in 0..n { |
| 38 | + let p = splitmix(&mut s); // true Bernoulli rate (ground truth) |
| 39 | + let n_obs = 1 + (splitmix(&mut s) * (max_obs - 1) as f64) as u32; |
| 40 | + let pos = (0..n_obs).filter(|_| splitmix(&mut s) < p).count() as f64; |
| 41 | + let f = pos / n_obs as f64; |
| 42 | + let c = n_obs as f64 / (n_obs as f64 + 1.0); // NARS horizon-1 confidence |
| 43 | + let energy = n_obs as f64 / max_obs as f64; // observation effort = energy proxy |
| 44 | + |
| 45 | + let h = nars_entropy(f, c); |
| 46 | + let e = c * (f - 0.5) + 0.5; |
| 47 | + let predict_true = e > 0.5; |
| 48 | + let m = 64; |
| 49 | + let hits = (0..m) |
| 50 | + .filter(|_| (splitmix(&mut s) < p) == predict_true) |
| 51 | + .count() as f64; |
| 52 | + |
| 53 | + entropies.push(h); |
| 54 | + accuracies.push(hits / m as f64); |
| 55 | + rungs[EntropyRung::from_entropy(h) as usize] += 1; |
| 56 | + quads[Quadrant::classify(h, energy) as usize] += 1; |
| 57 | + classes[entropy_class(h) as usize] += 1; |
| 58 | + } |
| 59 | + |
| 60 | + let rho = spearman(&entropies, &accuracies); |
| 61 | + let pct = |x: usize| 100.0 * x as f64 / n as f64; |
| 62 | + |
| 63 | + println!("VALIDATION ρ(entropy, empirical accuracy) = {rho:.4} (more negative ⇒ stronger reliability proxy)\n"); |
| 64 | + |
| 65 | + println!("Rung partition (syntax = high entropy / stimulus → pragmatics = low entropy / fact):"); |
| 66 | + println!(" Syntax {:6.1}% ({} edges)", pct(rungs[0]), rungs[0]); |
| 67 | + println!(" Semantics {:6.1}% ({} edges)", pct(rungs[1]), rungs[1]); |
| 68 | + println!(" Pragmatics {:6.1}% ({} edges)", pct(rungs[2]), rungs[2]); |
| 69 | + |
| 70 | + println!("\nQuadrant partition (entropy × energy, energy = observation effort):"); |
| 71 | + println!(" Staunen (hi-H, lo-E) {:6.1}%", pct(quads[0])); |
| 72 | + println!(" Confusion (hi-H, hi-E) {:6.1}%", pct(quads[1])); |
| 73 | + println!(" Boredom (lo-H, lo-E) {:6.1}%", pct(quads[2])); |
| 74 | + println!(" Wisdom (lo-H, hi-E) {:6.1}%", pct(quads[3])); |
| 75 | + |
| 76 | + println!("\n2-bit reliability class for CausalEdge64 spare bits [63:61]:"); |
| 77 | + println!( |
| 78 | + " class 0 (Wisdom) {:5.1}% | 1 {:5.1}% | 2 {:5.1}% | 3 (Staunen) {:5.1}%", |
| 79 | + pct(classes[0]), |
| 80 | + pct(classes[1]), |
| 81 | + pct(classes[2]), |
| 82 | + pct(classes[3]) |
| 83 | + ); |
| 84 | + |
| 85 | + println!("\nPearl-2³ SPO decomposition (3×palette-256 indices inside CausalEdge64, no re-quant):"); |
| 86 | + let demo: [(u8, u8, u8, u8, f64, f64, &str); 4] = [ |
| 87 | + (10, 20, 30, 0b111, 0.97, 0.95, "SPO crystalline fact"), |
| 88 | + (10, 20, 30, 0b101, 0.55, 0.20, "S_O fragment, raw stimulus"), |
| 89 | + (44, 88, 0, 0b011, 0.90, 0.80, "SP settled, O absent"), |
| 90 | + (7, 0, 0, 0b001, 0.50, 0.50, "S only, maximally ambiguous"), |
| 91 | + ]; |
| 92 | + for (si, pi, oi, mask, f, c, label) in demo { |
| 93 | + let pt = decompose_spo(si, pi, oi, mask, f, c); |
| 94 | + println!( |
| 95 | + " {label:<28} active={:?} H={:.3} {:<11?} basin_key=0x{:08X} class={}", |
| 96 | + pt.active, pt.entropy, pt.rung, pt.basin_key, pt.class |
| 97 | + ); |
| 98 | + } |
| 99 | +} |
0 commit comments