code wiki / (root) / nx_analyst_insight_gate.nx

nx_analyst_insight_gate.nx source

↩ module page · 126 lines · 7364 B

1// nx_analyst_insight_gate.nx -- F1003: prove the no-target insight miner finds real structure, names the 2// classes correctly, and CANNOT quietly p-hack. Two datasets: one with planted structure (every finding 3// class present, hand-checkable) and one of pure noise wide enough that the required family-wise level 4// falls off the critical table -- where the honest output is a REFUSAL, not a top-5 list. 5// D001-compliant verdict via nx_gate_verdict. license_tier: ORIGINAL expect_exit: 0 6import "nx_gate_verdict.nx" 7import "nx_analyst_insight.nx" 8 9const IG_N: i64 = 40 10const IG_WIDE: i64 = 30 11const IG_CAP: i64 = 65536 12 13func ig_lcg(s: i64) -> i64 { return (s * 1103515245 + 12345) & 0x7fffffff } 14func ig_has(hay: *u8, n: i64, needle: *u8) -> i64 { 15 var nl: i64 = 0 16 while needle[nl] != (0 as u8) { nl = nl + 1 } 17 var i: i64 = 0 18 while i + nl <= n { 19 var j: i64 = 0 20 var eq: i64 = 1 21 while j < nl { if hay[i+j] != needle[j] { eq = 0 } j = j + 1 } 22 if eq == 1 { return 1 } 23 i = i + 1 24 } 25 return 0 26} 27func ig_no(v: i64) -> i64 { if v == 0 { return 1 } return 0 } 28 29func main() -> i64 { 30 let ctr: *i64 = gv_ctr() 31 gv_head("nx_analyst_insight_gate -- point it at a dataset with NO target and see what it dares claim" as *u8) 32 33 // ---- DATASET A: planted structure, every finding class reachable ----------------------------- 34 // seq = 1..40 -> ID-LIKE (100pct unique) 35 // pow4 = seq^4 -> MONOTONE but strongly NON-LINEAR in seq (Spearman 1000 vs 36 // Pearson ~866: the whole point of having Spearman) 37 // lin = 3*seq + small -> plainly linear in seq 38 // konst= 7 -> CONSTANT 39 // skewy= ~10..29 with two 5000s -> mean 268 vs median ~19 (SKEW) and 2 values past 3 sigma 40 let seq: *i64 = sys_mmap(8 * IG_N) as *i64 41 let pw: *i64 = sys_mmap(8 * IG_N) as *i64 42 let ln: *i64 = sys_mmap(8 * IG_N) as *i64 43 let ko: *i64 = sys_mmap(8 * IG_N) as *i64 44 let sk: *i64 = sys_mmap(8 * IG_N) as *i64 45 var i: i64 = 0 46 while i < IG_N { 47 let v: i64 = i + 1 48 seq[i] = v 49 pw[i] = v * v * v * v 50 ln[i] = 3 * v + (i % 7) 51 ko[i] = 7 52 sk[i] = 10 + (i % 20) 53 i = i + 1 54 } 55 sk[5] = 5000 56 sk[17] = 5000 57 58 let cols: *i64 = sys_mmap(8 * 5) as *i64 59 let nms: *i64 = sys_mmap(8 * 5) as *i64 60 cols[0] = seq as i64 61 cols[1] = pw as i64 62 cols[2] = ln as i64 63 cols[3] = ko as i64 64 cols[4] = sk as i64 65 nms[0] = "seq" as *u8 as i64 66 nms[1] = "pow4" as *u8 as i64 67 nms[2] = "lin" as *u8 as i64 68 nms[3] = "konst" as *u8 as i64 69 nms[4] = "skewy" as *u8 as i64 70 71 let outA: *u8 = sys_mmap(IG_CAP) 72 let rA: i64 = ai_mine(cols, nms, 5, IG_N, outA, IG_CAP) 73 gv_puts(outA) 74 gv_puts("\n---\n" as *u8) 75 76 // 5 columns => C(5,2) = 10 pairs, and the report must SAY so before listing anything 77 gv_check("T1 the report DISCLOSES the hypothesis count (10 pairs) before any finding" as *u8, ig_has(outA, rA, "HYPOTHESES TESTED=10" as *u8), ctr) 78 gv_check("T2 and states the bar is FAMILY-WISE, not per-test .05" as *u8, ig_has(outA, rA, "FAMILY-WISE" as *u8), ctr) 79 gv_check("T3 finds the MONOTONE-BUT-NONLINEAR pair that a correlation-only scan under-reports" as *u8, ig_has(outA, rA, "[NONLINEAR-MONOTONE]" as *u8), ctr) 80 gv_check("T4 flags the constant column by name" as *u8, ig_has(outA, rA, "[CONSTANT] konst" as *u8), ctr) 81 gv_check("T5 flags an ID-LIKE column (correlations with a key are usually artefacts)" as *u8, ig_has(outA, rA, "[ID-LIKE]" as *u8), ctr) 82 gv_check("T6 finds a plain significant RELATIONSHIP too" as *u8, ig_has(outA, rA, "[RELATIONSHIP]" as *u8), ctr) 83 gv_check("T7 flags the skewed column (mean is not a typical value there)" as *u8, ig_has(outA, rA, "[SKEWED] skewy" as *u8), ctr) 84 gv_check("T8 flags its 3-sigma outliers" as *u8, ig_has(outA, rA, "[OUTLIERS] skewy" as *u8), ctr) 85 // negative control: under the cap, no truncation notice should appear 86 gv_check("T9 CONTROL findings fit under max_findings -> NO truncation notice" as *u8, ig_no(ig_has(outA, rA, "further finding" as *u8)), ctr) 87 88 // ---- DATASET B: pure noise, wide enough that the honest answer is a REFUSAL ------------------- 89 // 30 independent noise columns => C(30,2) = 435 pairs. Family-wise .05 needs .05/435, far stricter 90 // than the critical table holds -- so NO pairwise claim can be certified, and saying that plainly is 91 // the correct output. A miner that instead printed its top few correlations here would be inventing. 92 let ncols: *i64 = sys_mmap(8 * IG_WIDE) as *i64 93 let nnames: *i64 = sys_mmap(8 * IG_WIDE) as *i64 94 var c: i64 = 0 95 var s: i64 = 99 96 while c < IG_WIDE { 97 let col: *i64 = sys_mmap(8 * IG_N) as *i64 98 var k: i64 = 0 99 while k < IG_N { s = ig_lcg(s); col[k] = s % 100000; k = k + 1 } 100 ncols[c] = col as i64 101 nnames[c] = "noise" as *u8 as i64 102 c = c + 1 103 } 104 let outB: *u8 = sys_mmap(IG_CAP) 105 let rB: i64 = ai_mine(ncols, nnames, IG_WIDE, IG_N, outB, IG_CAP) 106 107 // ---- CORRECTNESS teeth. The first version of this gate checked only that each finding CLASS was 108 // PRESENT, and passed 12/12 on output that ranked five sentinel "undefined (constant column)" 109 // pseudo-correlations above every real finding and printed "137pct unique" for 40 rows. Presence is 110 // not correctness; these are the teeth that would have caught it. 111 gv_check("T13 no finding reports an UNDEFINED correlation (a sentinel is not a strong relationship)" as *u8, ig_no(ig_has(outA, rA, "undefined" as *u8)), ctr) 112 gv_check("T14a the constant column never appears as the RIGHT side of a relationship" as *u8, ig_no(ig_has(outA, rA, "<-> konst" as *u8)), ctr) 113 gv_check("T14b nor as the LEFT side" as *u8, ig_no(ig_has(outA, rA, "konst <->" as *u8)), ctr) 114 gv_check("T15a a distinct-percentage is a PERCENTAGE: seq is exactly 100pct unique over 40 distinct rows" as *u8, ig_has(outA, rA, "seq is 100pct unique" as *u8), ctr) 115 gv_check("T15b and never exceeds 100 (the HLL estimate used to read 137pct at n=40)" as *u8, ig_no(ig_has(outA, rA, "137pct" as *u8)), ctr) 116 gv_check("T16 the TOP-ranked finding is the nonlinear-monotone pair, not a sentinel artefact" as *u8, ig_has(outA, rA, " 1. [NONLINEAR-MONOTONE]" as *u8), ctr) 117 gv_check("T17 exactly 3 of 10 pairs survive (seq-lin, seq-pow4, pow4-lin); the 5 sentinel pairs are gone" as *u8, ig_has(outA, rA, "PAIRS SURVIVING CORRECTION=3 of 10" as *u8), ctr) 118 119 gv_check("T10 435 pairs need a level the table cannot supply -> says so, certifies NOTHING pairwise" as *u8, ig_has(outB, rB, "STRICTER THAN THE CRITICAL TABLE HOLDS" as *u8), ctr) 120 gv_check("T11 ADVERSARY pure noise -> PAIRS SURVIVING CORRECTION=0 (no manufactured findings)" as *u8, ig_has(outB, rB, "PAIRS SURVIVING CORRECTION=0" as *u8), ctr) 121 gv_check("T12 and when the finding list IS capped, the cap is DECLARED rather than silently truncating" as *u8, ig_has(outB, rB, "further finding" as *u8), ctr) 122 123 let rc: i64 = gv_verdict("ANALYST-INSIGHT-GATE" as *u8, ctr, "no-target insight mining that discloses its own multiplicity and refuses when the data cannot carry a claim" as *u8) 124 sys_exit(rc) 125 return rc 126}