code wiki / (root) / nx_axisvar.nx

nx_axisvar.nx source

↩ module page · 265 lines · 11493 B

1// nx_axisvar.nx -- DOES THIS JUDGE'S AXIS ACTUALLY VARY? A per-axis distribution census over a whole 2// directory of renders, so a fitness built on those axes can be justified by measurement instead of 3// by three hand-picked examples. 4// 5// nx_axisvar <dir> <prefix> <suffix> e.g. nx_axisvar /tmp bodyfit_ .png 6// 7// WHY IT EXISTS, MEASURED 2026-08-14: a body-evolution run improved 3 points in 10 generations and 8// LOST to random search at equal budget. A 5-point sweep suggested the reason -- contour_axis sat at 9// its ceiling and face_axis was binary -- but five renders is not a population, and changing a shipped 10// fitness on five samples is the same error as tuning until a green appears. The search had already 11// written ~132 renders; this reads ALL of them. 12// 13// ★★★IT REPORTS NUMBERS, NOT A VERDICT. An uncalibrated classifier must publish its distribution and 14// let the reader judge. The ONE verdict it does emit is not a threshold but a FACT: an axis with 15// distinct==1 took the same value on every single input, so it cannot discriminate anything, and 16// including it in a mean dilutes every other axis while contributing nothing. 17// 18// ★NO SILENT CAPS. The directory walk LOOPS getdents64 until it returns 0 -- one call is a prefix of a 19// directory, not a listing, and publishing that prefix's count as a total is how a census becomes a 20// sample nobody knows is partial. The distinct-value table is bounded and says so when it overflows. 21// ★A FAILED JUDGE IS COUNTED AND NAMED, never folded into the population: judged + failed must equal 22// matched, and the sum is printed so the partition can be checked. 23// exit 0 all matched files judged | 2 usage | 3 cannot open dir | 4 nothing matched | 5 some failed 24// license_tier: ORIGINAL. No hw writes (Rule 26). 25import "nx_syscalls.nx" 26import "nx_tool_run.nx" 27 28const AV_NAXIS: i64 = 4 29const AV_DIRBUF: i64 = 262144 30const AV_OUTCAP: i64 = 65536 31const AV_JUDGE_TIMEOUT_MS: i64 = 60000 32// bounded distinct-value table per axis; an overflow is REPORTED, never silently truncated. 33const AV_MAXDISTINCT: i64 = 64 34const AV_JUDGE: *u8 = "/volume1/homes/elderwesto/nishihost/nx_charjudge.elf" 35// dirent64: d_reclen is a u16 at byte 16, the NUL-terminated name starts at byte 19. 36const AV_DIRENT_RECLEN_OFF: i64 = 16 37const AV_DIRENT_NAME_OFF: i64 = 19 38 39func av_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 40func av_puts(s: *u8) -> i64 { sys_write(1, s, av_len(s)); return 0 } 41func av_pn(v: i64) -> i64 { 42 var m: i64 = v 43 if m < 0 { av_puts("-" as *u8); m = 0 - m } 44 let t: *u8 = sys_mmap(32) 45 var k: i64 = 0 46 if m == 0 { t[0] = 48 as u8; k = 1 } 47 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 48 let o: *u8 = sys_mmap(32) 49 var i: i64 = 0 50 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 51 sys_write(1, o, k) 52 return 0 53} 54func av_rdint(b: *u8, pos: *i64, end: i64) -> i64 { 55 var i: i64 = pos[0] 56 var go: i64 = 1 57 while go == 1 { 58 if i >= end { go = 0 } else { 59 let c: i64 = b[i] as i64 60 if c == 45 { go = 0 } else { if c >= 48 { if c <= 57 { go = 0 } else { i = i+1 } } else { i = i+1 } } 61 } 62 } 63 var sg: i64 = 1 64 if i < end { if (b[i] as i64) == 45 { sg = 0 - 1; i = i + 1 } } 65 var v: i64 = 0 66 var g2: i64 = 1 67 while g2 == 1 { 68 if i >= end { g2 = 0 } else { 69 let c2: i64 = b[i] as i64 70 if c2 >= 48 { if c2 <= 57 { v = v*10 + (c2-48); i = i+1 } else { g2 = 0 } } else { g2 = 0 } 71 } 72 } 73 pos[0] = i 74 return v*sg 75} 76// LAST anchored "<key>": in out[0..n). Anchored on the field name, taken positionally last -- a 77// greedy 'nearest number' parse silently starts measuring a different field than the one it names. 78func av_field(out: *u8, n: i64, key: *u8, found: *i64) -> i64 { 79 found[0] = 0 80 let kl: i64 = av_len(key) 81 var pos: i64 = 0 - 1 82 var j: i64 = 0 83 while j + kl + 3 <= n { 84 if out[j] == (34 as u8) { 85 var m: i64 = 0 86 var ok: i64 = 1 87 var scan: i64 = 1 88 while scan == 1 { 89 if m >= kl { scan = 0 } else { 90 if out[j + 1 + m] != key[m] { ok = 0; scan = 0 } else { m = m + 1 } 91 } 92 } 93 if ok == 1 { if out[j + 1 + kl] == (34 as u8) { if out[j + 2 + kl] == (58 as u8) { pos = j + 3 + kl } } } 94 } 95 j = j + 1 96 } 97 if pos < 0 { return 0 } 98 let p2: *i64 = sys_mmap(16) as *i64 99 p2[0] = pos 100 let v: i64 = av_rdint(out, p2, n) 101 found[0] = 1 102 return v 103} 104func av_starts(name: *u8, pre: *u8) -> i64 { 105 var i: i64 = 0 106 while pre[i] != (0 as u8) { 107 if name[i] == (0 as u8) { return 0 } 108 if name[i] != pre[i] { return 0 } 109 i = i + 1 110 } 111 return 1 112} 113func av_ends(name: *u8, suf: *u8) -> i64 { 114 let nl: i64 = av_len(name) 115 let sl: i64 = av_len(suf) 116 if sl > nl { return 0 } 117 var i: i64 = 0 118 while i < sl { 119 if name[nl - sl + i] != suf[i] { return 0 } 120 i = i + 1 121 } 122 return 1 123} 124// record v into axis a's distinct table; returns 1 if the table overflowed (reported, never hidden) 125func av_note(dist: *i64, dn: *i64, a: i64, v: i64) -> i64 { 126 let base: i64 = a * AV_MAXDISTINCT 127 var i: i64 = 0 128 while i < dn[a] { 129 if dist[base + i] == v { return 0 } 130 i = i + 1 131 } 132 if dn[a] >= AV_MAXDISTINCT { return 1 } 133 dist[base + dn[a]] = v 134 dn[a] = dn[a] + 1 135 return 0 136} 137func av_axname(a: i64) -> *u8 { 138 if a == 0 { return "composition" as *u8 } 139 if a == 1 { return "palette_axis" as *u8 } 140 if a == 2 { return "contour_axis" as *u8 } 141 return "face_axis" as *u8 142} 143 144func main(argc: i64, argv: *i64) -> i64 { 145 if argc < 4 { 146 av_puts("usage: nx_axisvar <dir> <prefix> <suffix> e.g. nx_axisvar /tmp bodyfit_ .png\n" as *u8) 147 sys_exit(2) 148 return 2 149 } 150 let dir: *u8 = argv[1] as *u8 151 let pre: *u8 = argv[2] as *u8 152 let suf: *u8 = argv[3] as *u8 153 let fd: i64 = sys_openat_rd(dir) 154 if fd < 0 { av_puts("AXISVAR REFUSE: cannot open directory\n" as *u8); sys_exit(3); return 3 } 155 156 let amin: *i64 = sys_mmap(AV_NAXIS*8) as *i64 157 let amax: *i64 = sys_mmap(AV_NAXIS*8) as *i64 158 let asum: *i64 = sys_mmap(AV_NAXIS*8) as *i64 159 let dist: *i64 = sys_mmap(AV_NAXIS*AV_MAXDISTINCT*8) as *i64 160 let dn: *i64 = sys_mmap(AV_NAXIS*8) as *i64 161 let ovf: *i64 = sys_mmap(AV_NAXIS*8) as *i64 162 var a: i64 = 0 163 while a < AV_NAXIS { amin[a] = 0; amax[a] = 0; asum[a] = 0; dn[a] = 0; ovf[a] = 0; a = a + 1 } 164 165 let dbuf: *u8 = sys_mmap(AV_DIRBUF) 166 let path: *u8 = sys_mmap(4096) 167 let out: *u8 = sys_mmap(AV_OUTCAP) 168 let olen: *i64 = sys_mmap(16) as *i64 169 let found: *i64 = sys_mmap(16) as *i64 170 let av3: *i64 = sys_mmap(64) as *i64 171 var matched: i64 = 0 172 var judged: i64 = 0 173 var failed: i64 = 0 174 175 // ★LOOP UNTIL getdents64 RETURNS 0. One call returns a PREFIX of a large directory; treating that 176 // as the listing is how a census silently becomes a sample and publishes a partial count as a total. 177 var more: i64 = 1 178 while more == 1 { 179 let n: i64 = sys_getdents64(fd, dbuf, AV_DIRBUF) 180 if n <= 0 { more = 0 } else { 181 var off: i64 = 0 182 while off < n { 183 let base: i64 = dbuf as i64 184 let rec: *u8 = (base + off) as *u8 185 let reclen: i64 = (rec[AV_DIRENT_RECLEN_OFF] as i64) + ((rec[AV_DIRENT_RECLEN_OFF+1] as i64) << 8) 186 let nm: *u8 = (base + off + AV_DIRENT_NAME_OFF) as *u8 187 if av_starts(nm, pre) == 1 { if av_ends(nm, suf) == 1 { 188 matched = matched + 1 189 var po: i64 = 0 190 var di: i64 = 0 191 while dir[di] != (0 as u8) { path[po] = dir[di]; po = po + 1; di = di + 1 } 192 path[po] = 47 as u8; po = po + 1 193 var ni: i64 = 0 194 while nm[ni] != (0 as u8) { path[po] = nm[ni]; po = po + 1; ni = ni + 1 } 195 path[po] = 0 as u8 196 av3[0] = AV_JUDGE as i64 197 av3[1] = path as i64 198 av3[2] = "axisvar" as *u8 as i64 199 av3[3] = 0 200 olen[0] = 0 201 if tr_run_capture_to(AV_JUDGE, av3, out, AV_OUTCAP, olen, AV_JUDGE_TIMEOUT_MS) != 0 { 202 failed = failed + 1 203 } else { 204 var got: i64 = 0 205 var k: i64 = 0 206 while k < AV_NAXIS { 207 let v: i64 = av_field(out, olen[0], av_axname(k), found) 208 if found[0] == 1 { 209 got = got + 1 210 if judged == 0 { amin[k] = v; amax[k] = v } 211 if v < amin[k] { amin[k] = v } 212 if v > amax[k] { amax[k] = v } 213 asum[k] = asum[k] + v 214 if av_note(dist, dn, k, v) == 1 { ovf[k] = 1 } 215 } 216 k = k + 1 217 } 218 if got == AV_NAXIS { judged = judged + 1 } else { failed = failed + 1 } 219 } 220 } } 221 if reclen <= 0 { off = n } else { off = off + reclen } 222 } 223 } 224 } 225 sys_close(fd) 226 227 av_puts("=== nx_axisvar dir=" as *u8); av_puts(dir) 228 av_puts(" prefix=" as *u8); av_puts(pre) 229 av_puts(" suffix=" as *u8); av_puts(suf); av_puts(" ===\n" as *u8) 230 av_puts("matched=" as *u8); av_pn(matched) 231 av_puts(" judged=" as *u8); av_pn(judged) 232 av_puts(" failed=" as *u8); av_pn(failed) 233 av_puts(" sum=" as *u8); av_pn(judged + failed) 234 if judged + failed == matched { av_puts(" partition=RECONCILES\n" as *u8) } else { av_puts(" partition=LEAK\n" as *u8) } 235 if judged == 0 { 236 av_puts("AXISVAR REFUSE: nothing judged -- a distribution over zero inputs is not a measurement\n" as *u8) 237 sys_exit(4) 238 return 4 239 } 240 a = 0 241 while a < AV_NAXIS { 242 av_puts(" " as *u8); av_puts(av_axname(a)) 243 av_puts(" min=" as *u8); av_pn(amin[a]) 244 av_puts(" max=" as *u8); av_pn(amax[a]) 245 av_puts(" range=" as *u8); av_pn(amax[a] - amin[a]) 246 av_puts(" mean=" as *u8); av_pn(asum[a] / judged) 247 av_puts(" distinct=" as *u8); av_pn(dn[a]) 248 if ovf[a] == 1 { av_puts("+ (table full, count is a FLOOR)" as *u8) } 249 // ★THE VERDICT IS BOUND TO ITS DENOMINATOR. At judged==1 every axis is trivially 'constant', 250 // and emitting that would be a true statement carrying no information -- the same shape as a 251 // tooth that passes on the empty set. n>1 is not a tuned threshold, it is the definition of 252 // having something to vary ACROSS. Caught by this organ's own first narrow run, which 253 // reported all four axes CONSTANT over a single render. 254 if judged > 1 { 255 // a FACT, not a threshold: one value over every input cannot discriminate anything, and 256 // averaging it in dilutes every other axis for nothing. 257 if dn[a] == 1 { av_puts(" CONSTANT-ACROSS-ALL-INPUTS" as *u8) } 258 } else { av_puts(" (n=1: variance UNDEFINED, not constant)" as *u8) } 259 av_puts("\n" as *u8) 260 a = a + 1 261 } 262 if failed > 0 { sys_exit(5); return 5 } 263 sys_exit(0) 264 return 0 265}