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}