code wiki / (root) / nx_analyst_infer_gate.nx

nx_analyst_infer_gate.nx source

↩ module page · 254 lines · 16972 B

1// nx_analyst_infer_gate.nx -- proves the analyst's statistical-inference layer with HAND-COMPUTED known 2// answers, every threshold arithmetic worked out by hand from rp^2*(n-2) > ceil(tcp^2*(1e6-rp^2)/1e6). 3// D001-compliant: emits its verdict through the ONE shared nx_gate_verdict lib (anchor "verdict="). 4// 5// The discriminating pair this gate exists for: the SAME two r values nx_analyst_report produces on its 6// capstone dataset (size r=994, age r=-172) are tested here at n=8 instead of that gate's n=300. At n=300 7// both are real; at n=8 size still is and age is NOISE. Sample size -- not effect size -- is what flips it, 8// which is exactly what the report could not previously say. Every positive tooth is paired with an 9// adversarial negative that MUST be rejected, so passing on true positives alone cannot make this green. 10// license_tier: ORIGINAL No hardware writes (Rule 26). 11import "nx_gate_verdict.nx" 12import "nx_analyst_infer.nx" 13 14const IG_NCOL: i64 = 2 15 16func ig_eq(a: i64, b: i64) -> i64 { if a == b { return 1 } return 0 } 17 18func main() -> i64 { 19 let ctr: *i64 = gv_ctr() 20 gv_head("nx_analyst_infer_gate -- is a reported correlation REAL, or noise dressed as a finding?" as *u8) 21 22 // ---- known-answer significance (df=6 => t_crit .05 two-tailed = 2.447) ---------------------- 23 // T1 r=+1000 n=8: lhs 1e6*6=6,000,000 vs rhs ceil(2447^2*0/1e6)=0 -> SIGNIFICANT 24 gv_check("T1 perfect r=+1000 at n=8 -> SIGNIFICANT" as *u8, ig_eq(ai_r_significant(1000, 8), 1), ctr) 25 // T2 ADVERSARY: r=-172 at n=8 (the report's own age correlation, at a sample size it never guarded). 26 // lhs 29,584*6=177,504 vs rhs ceil(5,987,809*970,416/1e6)=5,810,666 -> NOT significant 27 gv_check("T2 ADVERSARY r=-172 at n=8 (real at the report's n=300, NOISE here) -> NOT significant" as *u8, ig_eq(ai_r_significant(0 - 172, 8), 0), ctr) 28 // T3 r=994 n=8: lhs 988,036*6=5,928,216 vs rhs ceil(5,987,809*11,964/1e6)=71,639 -> SIGNIFICANT 29 gv_check("T3 r=994 at n=8 (the report's size driver, still real at tiny n) -> SIGNIFICANT" as *u8, ig_eq(ai_r_significant(994, 8), 1), ctr) 30 // T4 ADVERSARY, strong-LOOKING but under-powered: r=700 n=8 31 // lhs 490,000*6=2,940,000 vs rhs ceil(5,987,809*510,000/1e6)=3,053,783 -> NOT significant 32 gv_check("T4 ADVERSARY r=700 at n=8 (looks strong, under-powered) -> NOT significant" as *u8, ig_eq(ai_r_significant(700, 8), 0), ctr) 33 // T5 SAME r, more data: r=700 n=10, df=8 t_crit 2.306 34 // lhs 490,000*8=3,920,000 vs rhs ceil(5,317,636*510,000/1e6)=2,711,995 -> SIGNIFICANT 35 gv_check("T5 same r=700 at n=10 -> SIGNIFICANT (n is what changed, not the effect)" as *u8, ig_eq(ai_r_significant(700, 10), 1), ctr) 36 37 // ---- fail-closed boundaries ---------------------------------------------------------------- 38 gv_check("T6 n=2 -> CANNOT TEST (-1), never a silent 'not significant'" as *u8, ig_eq(ai_r_significant(900, 2), 0 - 1), ctr) 39 gv_check("T7 r=0 at n=1000 -> NOT significant (no effect, however much data)" as *u8, ig_eq(ai_r_significant(0, 1000), 0), ctr) 40 41 // ---- the large-n adversary pair, straddling the true critical r (~.062 at n=1000) ----------- 42 // df=998 -> largest tabulated df<=998 is 500 -> t_crit 1.965 (conservative fallback) 43 // T8 r=50 n=1000: lhs 2,500*998=2,495,000 vs rhs ceil(3,861,225*997,500/1e6)=3,851,572 -> NOT 44 gv_check("T8 ADVERSARY r=50 at n=1000 -> NOT significant (big n cannot rescue a tiny r)" as *u8, ig_eq(ai_r_significant(50, 1000), 0), ctr) 45 // T9 r=70 n=1000: lhs 4,900*998=4,890,200 vs rhs ceil(3,861,225*995,100/1e6)=3,842,305 -> SIG 46 gv_check("T9 r=70 at n=1000 -> SIGNIFICANT (tight pair with T8 around the true critical r)" as *u8, ig_eq(ai_r_significant(70, 1000), 1), ctr) 47 48 // ---- the critical table is DATA, and the fallback is conservative -------------------------- 49 gv_check("T10 t_crit(df=6) == 2447 read FROM analyst_tcrit.conf (not a literal)" as *u8, ig_eq(ai_tcrit(6), 2447), ctr) 50 gv_check("T11 t_crit(df=45) falls back to the df=40 row (2021) -- LARGER threshold = conservative" as *u8, ig_eq(ai_tcrit(45), 2021), ctr) 51 gv_check("T12 t_crit(df=0) == 0 -> caller treats as NOT significant (fail-closed)" as *u8, ig_eq(ai_tcrit(0), 0), ctr) 52 53 // ---- min-n is exactly the boundary (catches an off-by-one / broken binary search) ----------- 54 let need: i64 = ai_min_n(172) 55 var t13: i64 = 0 56 if need > 0 { if ai_r_significant(172, need) == 1 { if ai_r_significant(172, need - 1) == 0 { t13 = 1 } } } 57 gv_check("T13 min_n(r=172) is the EXACT flip point: significant at n, not at n-1" as *u8, t13, ctr) 58 gv_check("T14 min_n(r=0) == 0 -- no sample size ever makes a zero correlation real" as *u8, ig_eq(ai_min_n(0), 0), ctr) 59 60 // ---- THE PAYOFF: a key-driver picker that can REFUSE --------------------------------------- 61 let rs: *i64 = sys_mmap(8 * IG_NCOL) as *i64 62 rs[0] = 994 63 rs[1] = 0 - 172 64 gv_check("T15 guarded driver over [994,-172] at n=8 picks col 0 (the significant one)" as *u8, ig_eq(ai_key_driver_guarded(rs, IG_NCOL, 8), 0), ctr) 65 // ADVERSARY: the strongest |r| is NOT significant -> the honest answer is "no driver", not argmax 66 let rs2: *i64 = sys_mmap(8 * IG_NCOL) as *i64 67 rs2[0] = 0 - 172 68 rs2[1] = 150 69 gv_check("T16 ADVERSARY all-noise [-172,150] at n=8 -> -1 REFUSES to name a driver (argmax would say col 0)" as *u8, ig_eq(ai_key_driver_guarded(rs2, IG_NCOL, 8), 0 - 1), ctr) 70 // and the guard is not simply always-refusing: the SAME r's at n=200 clear the bar and it DOES pick. 71 // (df=198 -> t_crit row 120 = 1.980: lhs 29,584*198=5,857,632 > rhs 3,804,419 -> col 0 significant) 72 gv_check("T17 same [-172,150] at n=200 -> picks col 0 (the guard has power, it is not a blanket refusal)" as *u8, ig_eq(ai_key_driver_guarded(rs2, IG_NCOL, 200), 0), ctr) 73 74 // ---- the verdict string a human actually reads ---------------------------------------------- 75 let vb: *u8 = sys_mmap(512) 76 let sv: i64 = ai_r_verdict(0 - 172, 8, vb) 77 var t18: i64 = 0 78 if sv == 0 { if vb[0] == (78 as u8) { t18 = 1 } } // "NOT SIGNIFICANT..." 79 gv_check("T18 verdict text for r=-172 n=8 leads with NOT SIGNIFICANT" as *u8, t18, ctr) 80 81 // ---- F1002: the critical table audits ITSELF ----------------------------------------------- 82 // ~240 thresholds are hand-entered. A transposed digit would not look like an error, it would be a 83 // silently wrong verdict in one cell forever -- so the structure is re-derived mechanically instead. 84 gv_check("T19 critical table is STRUCTURALLY SOUND: 0 monotonicity violations over all 240 rows" as *u8, ig_eq(ai_tcrit_audit(), 0), ctr) 85 gv_check("T20a t_crit(alpha .01, df 10) == 3169 from the conf" as *u8, ig_eq(ai_tcrit_alpha(10, 10), 3169), ctr) 86 gv_check("T20b t_crit(alpha .001, df 10000) == 3291 = the normal-z asymptote, tabulated not assumed" as *u8, ig_eq(ai_tcrit_alpha(1, 10000), 3291), ctr) 87 gv_check("T20c the .05 path is unchanged by the 3-column format (df 6 still 2447)" as *u8, ig_eq(ai_tcrit(6), 2447), ctr) 88 89 // ---- F1002: Bonferroni -- the correction for picking the largest of m correlations ---------- 90 var t21: i64 = 1 91 if ai_alpha_for_m(1) != 50 { t21 = 0 } 92 if ai_alpha_for_m(2) != 20 { t21 = 0 } 93 if ai_alpha_for_m(5) != 10 { t21 = 0 } 94 if ai_alpha_for_m(10) != 2 { t21 = 0 } 95 if ai_alpha_for_m(50) != 1 { t21 = 0 } 96 gv_check("T21 per-test alpha for m comparisons picks the largest tabulated level <= .05/m" as *u8, t21, ctr) 97 gv_check("T22 m=51 needs a level stricter than the table holds -> 0 = REFUSE, never a laxer fallback" as *u8, ig_eq(ai_alpha_for_m(51), 0), ctr) 98 // r=700 n=10 is significant on its own (T5) but must NOT survive being the best of 20 candidates: 99 // at .002/df=8 the threshold is 4.501 -> lhs 3,920,000 vs rhs 10,332,091 100 gv_check("T23a r=700 at n=10 alone -> SIGNIFICANT (unchanged)" as *u8, ig_eq(ai_r_significant(700, 10), 1), ctr) 101 gv_check("T23b the SAME r as best-of-20 -> NOT significant (the correction actually bites)" as *u8, ig_eq(ai_r_sig_bonferroni(700, 10, 20), 0), ctr) 102 gv_check("T23c and m=1 leaves the uncorrected answer alone (not a blanket tightening)" as *u8, ig_eq(ai_r_sig_bonferroni(700, 10, 1), 1), ctr) 103 104 // ---- F1002: SPEARMAN -- monotone relationships Pearson understates -------------------------- 105 let xs: *i64 = sys_mmap(8 * 8) as *i64 106 let ys: *i64 = sys_mmap(8 * 8) as *i64 107 let yd: *i64 = sys_mmap(8 * 8) as *i64 108 var k: i64 = 0 109 while k < 8 { 110 let v: i64 = k + 1 111 xs[k] = v 112 ys[k] = v * v * v // strictly increasing, strongly non-linear 113 yd[k] = 0 - (v * v * v) // strictly decreasing 114 k = k + 1 115 } 116 gv_check("T24 Spearman on a strictly INCREASING cubic == +1000 exactly (ranks are identical)" as *u8, ig_eq(ai_spearman_milli(xs, ys, 8), 1000), ctr) 117 gv_check("T25 Spearman on a strictly DECREASING cubic == -1000 exactly" as *u8, ig_eq(ai_spearman_milli(xs, yd, 8), 0 - 1000), ctr) 118 // DISCRIMINATING: if Spearman just echoed Pearson this tooth would fail -- the curvature must cost 119 // Pearson something that ranking recovers in full. 120 var t26: i64 = 0 121 if am_pearson_milli(xs, ys, 8) < 1000 { t26 = 1 } 122 gv_check("T26 Pearson on that SAME cubic is < 1000 -- Spearman recovers what curvature costs it" as *u8, t26, ctr) 123 // ties: x=[5,5,7,3] -> ranks 2.5,2.5,4,1 -> doubled 5,5,8,2 (hand-computed) 124 let xt: *i64 = sys_mmap(8 * 4) as *i64 125 let rt: *i64 = sys_mmap(8 * 4) as *i64 126 xt[0] = 5 127 xt[1] = 5 128 xt[2] = 7 129 xt[3] = 3 130 ai_rank2_into(xt, 4, rt) 131 var t27: i64 = 1 132 if rt[0] != 5 { t27 = 0 } 133 if rt[1] != 5 { t27 = 0 } 134 if rt[2] != 8 { t27 = 0 } 135 if rt[3] != 2 { t27 = 0 } 136 gv_check("T27 tied values share the AVERAGE rank ([5,5,7,3] -> doubled 5,5,8,2)" as *u8, t27, ctr) 137 138 // ---- F1004: PARTIAL CORRELATION -- does a relationship survive a third column? --------------- 139 // Every value below is hand-derived from r(xy.z) = (r_xy - r_xz*r_yz)/sqrt((1-r_xz^2)(1-r_yz^2)). 140 // T28 control independent of both -> nothing to remove: den=isqrt(1e12)=1e6, num=600*1000 -> 600 141 gv_check("T28 a control uncorrelated with both leaves the correlation untouched (600 -> 600)" as *u8, ig_eq(ai_partial_milli(600, 0, 0), 600), ctr) 142 // T29 THE HEADLINE: z drives both, r_xy is exactly .9*.9 -- num = 810000 - 810000 = 0 143 gv_check("T29 when z fully explains both, the partial is EXACTLY 0 (r=810 was entirely shared cause)" as *u8, ig_eq(ai_partial_milli(810, 900, 900), 0), ctr) 144 // T30 SUPPRESSION is real and must not be clipped away: r_xy=0 but opposite links to z 145 // qx=qy=640000, den=640000, num = 0 - 600*(-600) = 360000 -> 360000*1000/640000 = 562 146 gv_check("T30 suppression: a raw r of 0 becomes 562 once z is controlled (partials can GROW)" as *u8, ig_eq(ai_partial_milli(0, 600, 0 - 600), 562), ctr) 147 // T31 a control perfectly correlated with x leaves no variance -> UNDEFINED, not a big number 148 gv_check("T31 a degenerate control returns UNDEFINED (|r|>1000), which the tests read as CANNOT TEST" as *u8, ig_eq(ai_partial_milli(500, 1000, 500), 0 - 2000), ctr) 149 gv_check("T32 a sentinel INPUT propagates as UNDEFINED rather than being treated as a correlation" as *u8, ig_eq(ai_partial_milli(0 - 2000, 0, 0), 0 - 2000), ctr) 150 151 // ---- F1004 end-to-end on real columns --------------------------------------------------------- 152 // z = 1..40 drives BOTH x and y. x,y therefore correlate strongly (~965) while neither explains the 153 // other: controlling for z must collapse it to ~0. This is the confound the old driver claim shipped. 154 let zz: *i64 = sys_mmap(8 * 40) as *i64 155 let xx: *i64 = sys_mmap(8 * 40) as *i64 156 let yy: *i64 = sys_mmap(8 * 40) as *i64 157 let nn: *i64 = sys_mmap(8 * 40) as *i64 158 var q: i64 = 0 159 var sd: i64 = 7 160 while q < 40 { 161 let v: i64 = q + 1 162 zz[q] = v 163 xx[q] = 3 * v + (q % 11) * 2 164 yy[q] = 5 * v + (q % 13) * 3 165 sd = (sd * 1103515245 + 12345) & 0x7fffffff 166 nn[q] = sd % 1000 167 q = q + 1 168 } 169 let cds: *i64 = sys_mmap(8 * 2) as *i64 170 let oz: *i64 = sys_mmap(8) as *i64 171 let op: *i64 = sys_mmap(8) as *i64 172 cds[0] = yy as i64 173 cds[1] = zz as i64 174 let rxy: i64 = am_pearson_milli(xx, yy, 40) 175 let found: i64 = ai_confound_scan(xx, cds, 2, 40, 0, oz, op) 176 var ap: i64 = op[0] 177 if ap < 0 { ap = 0 - ap } 178 var t33: i64 = 1 179 if found != 1 { t33 = 0 } 180 if oz[0] != 1 { t33 = 0 } 181 if rxy < 900 { t33 = 0 } 182 if ap > 100 { t33 = 0 } 183 gv_check("T33 x,y both driven by z: raw r>900 COLLAPSES to |partial|<100 once z is controlled" as *u8, t33, ctr) 184 // CONTROL: y really is driven by x, and z is unrelated noise -> the relationship must SURVIVE 185 let cds2: *i64 = sys_mmap(8 * 2) as *i64 186 cds2[0] = yy as i64 187 cds2[1] = nn as i64 188 let found2: i64 = ai_confound_scan(xx, cds2, 2, 40, 0, oz, op) 189 var ap2: i64 = op[0] 190 if ap2 < 0 { ap2 = 0 - ap2 } 191 var t34: i64 = 1 192 if found2 != 1 { t34 = 0 } 193 if ap2 < 800 { t34 = 0 } 194 gv_check("T34 CONTROL against unrelated noise the SAME pair survives (|partial|>800) -- not a blanket kill" as *u8, t34, ctr) 195 196 // ---- F1013: CONFIDENCE INTERVALS ------------------------------------------------------------- 197 // Worked by hand first, on paper, before any of this ran: r=.5, n=30 -> z=atanh(.5)=.5493, 198 // SE=1/sqrt(27)=.19245, d=1.96*SE=.3772, CI in z = [.1721, .9265], tanh -> [.1704, .7284]. 199 // So the organ must land on 170 and 728 permille. The +-3 band below is the DECLARED precision 200 // envelope of the fixed-point exp, not a fudge factor -- it is asserted tight enough that a real 201 // error in the range reduction or the Taylor sum cannot slip through it. 202 gv_check("T35a exp(0) == 1000000 exactly" as *u8, ig_eq(ai_exp_micro(0), 1000000), ctr) 203 var t35b: i64 = 0 204 let e2: i64 = ai_exp_micro(693147) 205 if e2 > 1999000 { if e2 < 2001000 { t35b = 1 } } 206 gv_check("T35b exp(ln2) == 2 to within 1 permille (range reduction + Taylor agree)" as *u8, t35b, ctr) 207 208 let clo: *i64 = sys_mmap(8) as *i64 209 let chi: *i64 = sys_mmap(8) as *i64 210 var t36: i64 = 0 211 if ai_r_ci(500, 30, 50, clo, chi) == 1 { 212 if clo[0] > 167 { if clo[0] < 174 { if chi[0] > 725 { if chi[0] < 732 { t36 = 1 } } } } 213 } 214 gv_check("T36 r=500 n=30 -> CI [170,728] as hand-computed from Fisher z" as *u8, t36, ctr) 215 // the interval must BRACKET the estimate -- a CI that excludes its own point estimate is broken 216 var t37: i64 = 0 217 if clo[0] < 500 { if chi[0] > 500 { t37 = 1 } } 218 gv_check("T37 the interval BRACKETS the point estimate" as *u8, t37, ctr) 219 // more data must NARROW it: same r at n=1000 220 let dlo: *i64 = sys_mmap(8) as *i64 221 let dhi: *i64 = sys_mmap(8) as *i64 222 var t38: i64 = 0 223 if ai_r_ci(500, 1000, 50, dlo, dhi) == 1 { 224 if (dhi[0] - dlo[0]) < (chi[0] - clo[0]) { t38 = 1 } 225 } 226 gv_check("T38 the SAME r at n=1000 gives a strictly NARROWER interval than at n=30" as *u8, t38, ctr) 227 // r=0 must be symmetric about zero 228 var t39: i64 = 0 229 if ai_r_ci(0, 30, 50, dlo, dhi) == 1 { if dlo[0] == (0 - dhi[0]) { t39 = 1 } } 230 gv_check("T39 r=0 gives an interval symmetric about zero" as *u8, t39, ctr) 231 gv_check("T40 n=3 cannot form a Fisher-z interval (needs n-3>=1) -> 0, not a fabricated range" as *u8, ig_eq(ai_r_ci(500, 3, 50, dlo, dhi), 0), ctr) 232 var t41: i64 = 0 233 if ai_r_ci(1000, 30, 50, dlo, dhi) == 1 { if dlo[0] == 1000 { if dhi[0] == 1000 { t41 = 1 } } } 234 gv_check("T41 |r|=1000 degenerates to the point itself rather than dividing by zero" as *u8, t41, ctr) 235 236 // ---- T42: the INTERVAL and the TEST are computed by completely different routes (Fisher z + an 237 // integer exp, versus a squared-t comparison against a table). Their agreement about whether zero is 238 // a plausible value is a real cross-check, not a restatement. 239 var t42: i64 = 1 240 if ai_r_ci(500, 30, 50, dlo, dhi) == 1 { 241 if dlo[0] <= 0 { t42 = 0 } // CI excludes 0 ... 242 if ai_r_significant(500, 30) != 1 { t42 = 0 } // ... and the test says significant 243 } else { t42 = 0 } 244 if ai_r_ci(50, 30, 50, dlo, dhi) == 1 { 245 if dlo[0] > 0 { t42 = 0 } // CI includes 0 ... 246 if dhi[0] < 0 { t42 = 0 } 247 if ai_r_significant(50, 30) != 0 { t42 = 0 } // ... and the test says not significant 248 } else { t42 = 0 } 249 gv_check("T42 CROSS-CHECK: CI excludes 0 exactly when the t-test calls it significant, and includes 0 when it does not" as *u8, t42, ctr) 250 251 let rc: i64 = gv_verdict("ANALYST-INFER-GATE" as *u8, ctr, "significance, multiple-comparison correction and rank correlation, all fail-closed and integer-exact" as *u8) 252 sys_exit(rc) 253 return rc 254}