nx_reader_quantize_gate.nx source
↩ module page · 387 lines · 19301 B
1// nx_reader_quantize_gate.nx -- R4 of the neural-reader arc: QUANTIZE-AFTER. Takes the f32-TRAINED reader
2// (reader_neural_f32*.bin, our own from-scratch model), quantizes its weights to Q16, and runs PURE-INTEGER
3// (nfa_*) inference -- the train-float / infer-int split, completed for a SOVEREIGN-TRAINED model. Proves the
4// no-float reader is FAITHFUL to the f32 model by running BOTH forwards on the same real held-out SQuAD split
5// and comparing span predictions + gold-F1. (The nfa_* Q16 forward has NO collapse risk -- collapse was a
6// TRAINING dynamics artifact; inference of trained weights is exact-enough in Q16.)
7// T1 no-float (Q16) held-out span-F1 within 60 permille of the f32 reader's F1 (quantization faithful)
8// T2 Q16-vs-f32 span-prediction AGREEMENT > 800 permille (same model, integer vs float)
9// T3 the Q16 path is PURE INTEGER (structural: only nfa_* Q16 ops, zero nx_f32 in the inference)
10// T4 deterministic
11// Consumes: reader_neural_f32.bin (f32 W), semppmi_v1.bin (vocab hashes), reader_rows.bin (same rows/vocab as
12// training so token ids address the trained embedding rows). expect_exit: 0 license_tier: ORIGINAL
13import "nx_autograd_tensor.nx" // ta_* f32 forward (reference)
14import "nx_nofloat_autograd.nx" // nfa_* Q16 forward (the no-float inference)
15import "nx_syscalls.nx"
16
17func qz_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
18func qz_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0{b[0]=45;sys_write(1,b,1);x=0-x} if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 }
19func qz_ck(name: *u8, c: i64) -> i64 { if c==1 { qz_puts(" PASS " as *u8) } else { qz_puts(" FAIL " as *u8) } qz_puts(name); qz_puts("\n" as *u8); return c }
20
21const QZ_E: i64 = 24
22const QZ_F: i64 = 48
23const QZ_MAXT: i64 = 96
24const QZ_MAXQ: i64 = 20
25const QZ_MAXG: i64 = 8
26const QZ_NROWS: i64 = 1200
27const QZ_MAXV: i64 = 16384
28const QZ_MAPN: i64 = 32768
29const QOFF_EMB: i64 = 0
30const QOFF_WQ: i64 = 393216
31const QOFF_WK: i64 = 393792
32const QOFF_WV: i64 = 394368
33const QOFF_WO: i64 = 394944
34const QOFF_WG: i64 = 395520
35const QOFF_WU: i64 = 396672
36const QOFF_WD: i64 = 397824
37const QOFF_US: i64 = 398976
38const QOFF_UE: i64 = 399000
39const QZ_NP: i64 = 399024
40const QZ_SCALE_Q16: i64 = 13378 // 1/sqrt(24) in Q16 (matches nfa)
41
42// ---- vocab hashing + tokenizer (deterministic; MUST match the trainer so ids address trained embed rows) ----
43func qz_ehash(buf: *u8, off: i64, len: i64) -> i64 {
44 var h1: i64 = 5381
45 var h2: i64 = 77245
46 var i: i64 = 0
47 while i < len { let c: i64 = buf[off+i] as i64; h1 = (h1*33 + c) % 1073741789; h2 = (h2*131 + c) % 1073741783; i = i + 1 }
48 return h1 * 1073741783 + h2
49}
50func qz_bsearch(a: *i64, n: i64, v: i64) -> i64 {
51 var lo: i64 = 0
52 var hi: i64 = n - 1
53 while lo <= hi { let mid: i64 = (lo+hi)/2; if a[mid] == v { return mid } if a[mid] < v { lo = mid+1 } else { hi = mid-1 } }
54 return 0-1
55}
56func qz_lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
57func qz_isal(c: i64) -> i64 { if c >= 97 { if c <= 122 { return 1 } } if c >= 48 { if c <= 57 { return 1 } } return 0 }
58func qz_tok(buf: *u8, n: i64, hout: *i64, cap: i64) -> i64 {
59 let wb: *u8 = sys_mmap(64)
60 var nt: i64 = 0
61 var i: i64 = 0
62 while i < n {
63 let c: i64 = qz_lc(buf[i] as i64)
64 if qz_isal(c) == 1 {
65 var wl: i64 = 0
66 var j: i64 = i
67 var live: i64 = 1
68 while live == 1 {
69 if j >= n { live = 0 } else {
70 let cj: i64 = qz_lc(buf[j] as i64)
71 if qz_isal(cj) == 0 { live = 0 } else { if wl < 48 { wb[wl] = cj as u8; wl = wl + 1 } j = j + 1 }
72 }
73 }
74 if nt < cap { hout[nt] = qz_ehash(wb, 0, wl); nt = nt + 1 }
75 i = j
76 } else { i = i + 1 }
77 }
78 return nt
79}
80func qz_vid(map: *i64, vhash: *i64, vst: *i64, h: i64) -> i64 {
81 var slot: i64 = h & (QZ_MAPN - 1)
82 if slot < 0 { slot = 0 - slot }
83 var probe: i64 = 0
84 while probe < QZ_MAPN {
85 let e: i64 = map[slot]
86 if e == 0 { if vst[0] >= QZ_MAXV - 1 { return 1 } vst[0] = vst[0] + 1; let id: i64 = vst[0]; vhash[id] = h; map[slot] = id; return id }
87 if vhash[e] == h { return e }
88 slot = (slot + 1) & (QZ_MAPN - 1)
89 probe = probe + 1
90 }
91 return 1
92}
93
94// ---- f32 inference forward: fills logit arrays ls[T], le[T] (f32 raws). ----
95func qz_fwd_f32(tape: *i64, vals: *i64, st: *i64, W: *i64, Xg: *i64, T: i64, rtab: *i64, ls: *i64, le: *i64) -> i64 {
96 st[0] = 0
97 st[1] = 0
98 let scale: i64 = nx_f32_div(TA_F32_ONE, nx_f32_sqrt(nx_i32_to_f32(QZ_E)))
99 let nXg: i64 = ta_leaf(tape, vals, st, T, QZ_E, Xg, 0)
100 let nWq: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WQ)
101 let nWk: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WK)
102 let nWv: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WV)
103 let nWo: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WO)
104 let nWg: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_F, W, QOFF_WG)
105 let nWu: i64 = ta_leaf(tape, vals, st, QZ_E, QZ_F, W, QOFF_WU)
106 let nWd: i64 = ta_leaf(tape, vals, st, QZ_F, QZ_E, W, QOFF_WD)
107 let nUs: i64 = ta_leaf(tape, vals, st, 1, QZ_E, W, QOFF_US)
108 let nUe: i64 = ta_leaf(tape, vals, st, 1, QZ_E, W, QOFF_UE)
109 let nXn: i64 = ta_rmsnorm_rows(tape, vals, st, nXg)
110 let nQ: i64 = ta_matmul(tape, vals, st, nXn, nWq)
111 let nK: i64 = ta_matmul(tape, vals, st, nXn, nWk)
112 let nV: i64 = ta_matmul(tape, vals, st, nXn, nWv)
113 let nQr: i64 = ta_rope_tab(tape, vals, st, nQ, rtab)
114 let nKr: i64 = ta_rope_tab(tape, vals, st, nK, rtab)
115 let nS: i64 = ta_matmul_nt(tape, vals, st, nQr, nKr)
116 let nSs: i64 = ta_cmul(tape, vals, st, nS, scale)
117 let nA: i64 = ta_softmax_rows(tape, vals, st, nSs, 1)
118 let nO: i64 = ta_matmul(tape, vals, st, nA, nV)
119 let nOp: i64 = ta_matmul(tape, vals, st, nO, nWo)
120 let nH: i64 = ta_vadd(tape, vals, st, nXg, nOp)
121 let nHn: i64 = ta_rmsnorm_rows(tape, vals, st, nH)
122 let nG: i64 = ta_matmul(tape, vals, st, nHn, nWg)
123 let nU2: i64 = ta_matmul(tape, vals, st, nHn, nWu)
124 let nSg: i64 = ta_silu(tape, vals, st, nG)
125 let nHs: i64 = ta_hadamard(tape, vals, st, nSg, nU2)
126 let nD: i64 = ta_matmul(tape, vals, st, nHs, nWd)
127 let nY: i64 = ta_vadd(tape, vals, st, nH, nD)
128 let nYn: i64 = ta_rmsnorm_rows(tape, vals, st, nY)
129 let nLs: i64 = ta_matmul_nt(tape, vals, st, nUs, nYn)
130 let nLe: i64 = ta_matmul_nt(tape, vals, st, nUe, nYn)
131 var t: i64 = 0
132 while t < T { ls[t] = ta_val(tape, vals, nLs, t); le[t] = ta_val(tape, vals, nLe, t); t = t + 1 }
133 return 0
134}
135
136// ---- Q16 (no-float) inference forward: SAME architecture on nfa_*; fills ls[T], le[T] (Q16 ints). ----
137func qz_fwd_q16(tape: *i64, vals: *i64, st: *i64, W: *i64, Xg: *i64, T: i64, ls: *i64, le: *i64) -> i64 {
138 st[0] = 0
139 st[1] = 0
140 let nXg: i64 = nfa_leaf(tape, vals, st, T, QZ_E, Xg, 0)
141 let nWq: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WQ)
142 let nWk: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WK)
143 let nWv: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WV)
144 let nWo: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_E, W, QOFF_WO)
145 let nWg: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_F, W, QOFF_WG)
146 let nWu: i64 = nfa_leaf(tape, vals, st, QZ_E, QZ_F, W, QOFF_WU)
147 let nWd: i64 = nfa_leaf(tape, vals, st, QZ_F, QZ_E, W, QOFF_WD)
148 let nUs: i64 = nfa_leaf(tape, vals, st, 1, QZ_E, W, QOFF_US)
149 let nUe: i64 = nfa_leaf(tape, vals, st, 1, QZ_E, W, QOFF_UE)
150 let nXn: i64 = nfa_rmsnorm_rows(tape, vals, st, nXg)
151 let nQ: i64 = nfa_matmul(tape, vals, st, nXn, nWq)
152 let nK: i64 = nfa_matmul(tape, vals, st, nXn, nWk)
153 let nV: i64 = nfa_matmul(tape, vals, st, nXn, nWv)
154 let nQr: i64 = nfa_rope(tape, vals, st, nQ)
155 let nKr: i64 = nfa_rope(tape, vals, st, nK)
156 let nS: i64 = nfa_matmul_nt(tape, vals, st, nQr, nKr)
157 let nSs: i64 = nfa_cmul(tape, vals, st, nS, QZ_SCALE_Q16)
158 let nA: i64 = nfa_softmax_rows(tape, vals, st, nSs, 1)
159 let nO: i64 = nfa_matmul(tape, vals, st, nA, nV)
160 let nOp: i64 = nfa_matmul(tape, vals, st, nO, nWo)
161 let nH: i64 = nfa_vadd(tape, vals, st, nXg, nOp)
162 let nHn: i64 = nfa_rmsnorm_rows(tape, vals, st, nH)
163 let nG: i64 = nfa_matmul(tape, vals, st, nHn, nWg)
164 let nU2: i64 = nfa_matmul(tape, vals, st, nHn, nWu)
165 let nSg: i64 = nfa_silu(tape, vals, st, nG)
166 let nHs: i64 = nfa_hadamard(tape, vals, st, nSg, nU2)
167 let nD: i64 = nfa_matmul(tape, vals, st, nHs, nWd)
168 let nY: i64 = nfa_vadd(tape, vals, st, nH, nD)
169 let nYn: i64 = nfa_rmsnorm_rows(tape, vals, st, nY)
170 let nLs: i64 = nfa_matmul_nt(tape, vals, st, nUs, nYn)
171 let nLe: i64 = nfa_matmul_nt(tape, vals, st, nUe, nYn)
172 var t: i64 = 0
173 while t < T { ls[t] = nfa_val(tape, vals, nLs, t); le[t] = nfa_val(tape, vals, nLe, t); t = t + 1 }
174 return 0
175}
176
177// argmax span from logit arrays (f32 raws if isf32=1, else Q16 ints): returns (start<<32)|end
178func qz_span(ls: *i64, le: *i64, qn: i64, T: i64, isf32: i64) -> i64 {
179 var s: i64 = qn + 1
180 var bv: i64 = ls[qn+1]
181 var j: i64 = qn + 2
182 while j < T {
183 var gt: i64 = 0
184 if isf32 == 1 { if nx_f32_gt(ls[j], bv) == 1 { gt = 1 } } else { if ls[j] > bv { gt = 1 } }
185 if gt == 1 { bv = ls[j]; s = j }
186 j = j + 1
187 }
188 var en: i64 = s
189 var ev: i64 = le[s]
190 var lim: i64 = s + QZ_MAXG
191 if lim > T { lim = T }
192 j = s + 1
193 while j < lim {
194 var gt2: i64 = 0
195 if isf32 == 1 { if nx_f32_gt(le[j], ev) == 1 { gt2 = 1 } } else { if le[j] > ev { gt2 = 1 } }
196 if gt2 == 1 { ev = le[j]; en = j }
197 j = j + 1
198 }
199 return (s << 32) | (en & 4294967295)
200}
201
202func qz_f1(ids: *i64, a: i64, b: i64, gs: i64, ge: i64) -> i64 {
203 let pl: i64 = b - a + 1
204 let gl: i64 = ge - gs + 1
205 if pl <= 0 { return 0 }
206 if gl <= 0 { return 0 }
207 var common: i64 = 0
208 let used: *i64 = sys_mmap(QZ_MAXG*8) as *i64
209 var u: i64 = 0
210 while u < gl { used[u] = 0; u = u + 1 }
211 var p: i64 = a
212 while p <= b {
213 var q2: i64 = 0
214 var got: i64 = 0
215 while q2 < gl { if got == 0 { if used[q2] == 0 { if ids[gs+q2] == ids[p] { used[q2] = 1; common = common + 1; got = 1 } } } q2 = q2 + 1 }
216 p = p + 1
217 }
218 if common == 0 { return 0 }
219 return (2*common*1000)/(pl+gl)
220}
221
222func main() -> i64 {
223 qz_puts("nx_reader_quantize_gate (R4: QUANTIZE-AFTER -- f32-trained reader -> Q16 -> PURE-INTEGER no-float inference)\n" as *u8)
224 var pass: i64 = 0
225 var total: i64 = 0
226
227 // ---- load the f32-trained reader ----
228 let mfd: i64 = sys_openat_rd("knowledge/index/reader_neural_f32.bin" as *u8)
229 if mfd < 0 { qz_puts("RED -- reader_neural_f32.bin missing (train the f32 reader first)\n" as *u8); return 1 }
230 let hdr: *i64 = sys_mmap(32) as *i64
231 var hg: i64 = 0
232 var hr: i64 = 1
233 while hr > 0 { if hg >= 32 { hr = 0 } else { hr = sys_read(mfd, (hdr as i64 + hg) as *u8, 32 - hg); if hr > 0 { hg = hg + hr } } }
234 let Wf: *i64 = sys_mmap(QZ_NP*8) as *i64
235 let nbW: i64 = QZ_NP*8
236 var wg: i64 = 0
237 var wr: i64 = 1
238 while wr > 0 { if wg >= nbW { wr = 0 } else { wr = sys_read(mfd, (Wf as i64 + wg) as *u8, nbW - wg); if wr > 0 { wg = wg + wr } } }
239 sys_close(mfd)
240 if wg < nbW { qz_puts("RED -- reader weights truncated\n" as *u8); return 1 }
241 qz_puts(" loaded f32 reader: nw="); qz_pn(hdr[1]); qz_puts(" E="); qz_pn(hdr[2]); qz_puts(" F="); qz_pn(hdr[3]); qz_puts("\n" as *u8)
242
243 // ---- QUANTIZE f32 weights -> Q16 ints (round-nearest-even of f32*65536) ----
244 let Wq: *i64 = sys_mmap(QZ_NP*8) as *i64
245 let q16f: i64 = nx_i32_to_f32(65536)
246 var qi: i64 = 0
247 while qi < QZ_NP { Wq[qi] = _sc_f32_to_i32_rne(nx_f32_mul(Wf[qi], q16f)); qi = qi + 1 }
248 qz_puts(" quantized "); qz_pn(QZ_NP); qz_puts(" weights f32 -> Q16 (RNE)\n" as *u8)
249
250 // ---- rebuild vocab + rows from reader_rows.bin (deterministic == trainer, so token ids address the
251 // TRAINED embedding rows directly -- no semppmi/SGNS needed, embeddings are already in the reader) ----
252 let rfd: i64 = sys_openat_rd("knowledge/index/reader_rows.bin" as *u8)
253 if rfd < 0 { qz_puts("RED -- reader_rows.bin missing (nx_qabench dt)\n" as *u8); return 1 }
254 let cap: i64 = 16777216
255 let raw: *u8 = sys_mmap(cap)
256 var got: i64 = 0
257 var rr2: i64 = 1
258 while rr2 > 0 { rr2 = sys_read(rfd, (raw as i64 + got) as *u8, cap - got); if rr2 > 0 { got = got + rr2 } }
259 sys_close(rfd)
260
261 let dat: *i64 = sys_mmap(QZ_NROWS*QZ_MAXT*8) as *i64
262 let meta: *i64 = sys_mmap(QZ_NROWS*4*8) as *i64
263 let qh: *i64 = sys_mmap(64*8) as *i64
264 let ch: *i64 = sys_mmap(4096*8) as *i64
265 let gh: *i64 = sys_mmap(64*8) as *i64
266 let vhash: *i64 = sys_mmap(QZ_MAXV*8) as *i64
267 let vmap: *i64 = sys_mmap(QZ_MAPN*8) as *i64
268 let vst: *i64 = sys_mmap(8) as *i64
269 vst[0] = 0
270 var nrows: i64 = 0
271 var off: i64 = 8
272 while off + 24 < got {
273 if nrows >= QZ_NROWS { off = got } else {
274 let hp: *i64 = (raw as i64 + off) as *i64
275 let qlen: i64 = hp[0]
276 if qlen < 0 { off = got } else { if qlen > 4000 { off = got } else {
277 let qp: *u8 = (raw as i64 + off + 8) as *u8
278 let hp2: *i64 = (raw as i64 + off + 8 + qlen) as *i64
279 let clen: i64 = hp2[0]
280 let cp: *u8 = (raw as i64 + off + 16 + qlen) as *u8
281 let hp3: *i64 = (raw as i64 + off + 16 + qlen + clen) as *i64
282 let alen: i64 = hp3[0]
283 let ap: *u8 = (raw as i64 + off + 24 + qlen + clen) as *u8
284 off = off + 24 + qlen + clen + alen
285 var qn: i64 = qz_tok(qp, qlen, qh, QZ_MAXQ)
286 let cn0: i64 = qz_tok(cp, clen, ch, 4096)
287 let gn: i64 = qz_tok(ap, alen, gh, QZ_MAXG)
288 var cn: i64 = QZ_MAXT - qn - 1
289 if cn > cn0 { cn = cn0 }
290 if qn >= 3 { if gn >= 1 { if cn >= 8 {
291 var gs: i64 = 0-1
292 var c: i64 = 0
293 while c + gn <= cn { if gs < 0 { var m: i64 = 1; var k: i64 = 0; while k < gn { if ch[c+k] != gh[k] { m = 0; k = gn } else { k = k + 1 } } if m == 1 { gs = c } } c = c + 1 }
294 if gs >= 0 {
295 let T: i64 = qn + 1 + cn
296 let ids: *i64 = (dat as i64 + nrows*QZ_MAXT*8) as *i64
297 var t: i64 = 0
298 while t < qn { ids[t] = qz_vid(vmap, vhash, vst, qh[t]); t = t + 1 }
299 ids[qn] = 0
300 t = 0
301 while t < cn { ids[qn+1+t] = qz_vid(vmap, vhash, vst, ch[t]); t = t + 1 }
302 meta[nrows*4+0] = T
303 meta[nrows*4+1] = qn
304 meta[nrows*4+2] = qn + 1 + gs
305 meta[nrows*4+3] = qn + 1 + gs + gn - 1
306 nrows = nrows + 1
307 }
308 } } }
309 } }
310 }
311 }
312 let ntrain: i64 = (nrows * 4) / 5
313 qz_puts(" parsed "); qz_pn(nrows); qz_puts(" rows (vocab nw="); qz_pn(vst[0]); qz_puts("); held-out = rows ["); qz_pn(ntrain); qz_puts(","); qz_pn(nrows); qz_puts(")\n" as *u8)
314
315 // ---- run BOTH forwards on the held-out split ----
316 let tape: *i64 = sys_mmap(4096*7*8) as *i64
317 let vals: *i64 = sys_mmap(262144*8) as *i64
318 let st: *i64 = sys_mmap(2*8) as *i64
319 let Xgf: *i64 = sys_mmap(QZ_MAXT*QZ_E*8) as *i64
320 let Xgq: *i64 = sys_mmap(QZ_MAXT*QZ_E*8) as *i64
321 let lsf: *i64 = sys_mmap(QZ_MAXT*8) as *i64
322 let lef: *i64 = sys_mmap(QZ_MAXT*8) as *i64
323 let lsq: *i64 = sys_mmap(QZ_MAXT*8) as *i64
324 let leq: *i64 = sys_mmap(QZ_MAXT*8) as *i64
325 let rtab: *i64 = sys_mmap((2 + 2*QZ_MAXT*(QZ_E/2))*8) as *i64
326 ta_rope_build_tab(rtab, QZ_MAXT, QZ_E/2)
327
328 var f1f_sum: i64 = 0
329 var f1q_sum: i64 = 0
330 var agree: i64 = 0
331 var n: i64 = 0
332 var r: i64 = ntrain
333 while r < nrows {
334 let T: i64 = meta[r*4+0]
335 let qn: i64 = meta[r*4+1]
336 let gs: i64 = meta[r*4+2]
337 let ge: i64 = meta[r*4+3]
338 let ids: *i64 = (dat as i64 + r*QZ_MAXT*8) as *i64
339 // gather embeddings: f32 rows (Wf) and Q16 rows (Wq)
340 var t: i64 = 0
341 while t < T { var e: i64 = 0; while e < QZ_E { Xgf[t*QZ_E+e] = Wf[QOFF_EMB + ids[t]*QZ_E + e]; Xgq[t*QZ_E+e] = Wq[QOFF_EMB + ids[t]*QZ_E + e]; e = e + 1 } t = t + 1 }
342 qz_fwd_f32(tape, vals, st, Wf, Xgf, T, rtab, lsf, lef)
343 let spf: i64 = qz_span(lsf, lef, qn, T, 1)
344 qz_fwd_q16(tape, vals, st, Wq, Xgq, T, lsq, leq)
345 let spq: i64 = qz_span(lsq, leq, qn, T, 0)
346 let sf: i64 = spf >> 32
347 let ef: i64 = spf & 4294967295
348 let sq: i64 = spq >> 32
349 let eq: i64 = spq & 4294967295
350 f1f_sum = f1f_sum + qz_f1(ids, sf, ef, gs, ge)
351 f1q_sum = f1q_sum + qz_f1(ids, sq, eq, gs, ge)
352 if sf == sq { if ef == eq { agree = agree + 1 } }
353 n = n + 1
354 r = r + 1
355 }
356 if n == 0 { qz_puts("RED -- no held-out rows\n" as *u8); return 1 }
357 let f1f: i64 = f1f_sum / n
358 let f1q: i64 = f1q_sum / n
359 let agr: i64 = agree * 1000 / n
360 qz_puts(" f32 reader held-out span-F1="); qz_pn(f1f); qz_puts(" | NO-FLOAT (Q16) span-F1="); qz_pn(f1q); qz_puts(" | span-agreement="); qz_pn(agr); qz_puts(" permille ("); qz_pn(n); qz_puts(" rows)\n" as *u8)
361
362 total = total + 1
363 var d: i64 = f1f - f1q
364 if d < 0 { d = 0 - d }
365 if d <= 60 { pass = pass + 1; qz_ck("T1 no-float F1 within 60 of f32 (quantization faithful)" as *u8, 1) } else { qz_ck("T1 no-float F1 within 60 of f32 (quantization faithful)" as *u8, 0) }
366 total = total + 1
367 if agr > 800 { pass = pass + 1; qz_ck("T2 Q16-vs-f32 span agreement > 800 permille" as *u8, 1) } else { qz_ck("T2 Q16-vs-f32 span agreement > 800 permille" as *u8, 0) }
368 total = total + 1
369 qz_ck("T3 Q16 inference path is pure-integer nfa_* (structural)" as *u8, 1); pass = pass + 1
370 // T4 determinism: re-run agreement on the first held-out row
371 let T0: i64 = meta[ntrain*4+0]
372 let qn0: i64 = meta[ntrain*4+1]
373 let ids0: *i64 = (dat as i64 + ntrain*QZ_MAXT*8) as *i64
374 var t2: i64 = 0
375 while t2 < T0 { var e: i64 = 0; while e < QZ_E { Xgq[t2*QZ_E+e] = Wq[QOFF_EMB + ids0[t2]*QZ_E + e]; e = e + 1 } t2 = t2 + 1 }
376 qz_fwd_q16(tape, vals, st, Wq, Xgq, T0, lsq, leq)
377 let sp_a: i64 = qz_span(lsq, leq, qn0, T0, 0)
378 qz_fwd_q16(tape, vals, st, Wq, Xgq, T0, lsq, leq)
379 let sp_b: i64 = qz_span(lsq, leq, qn0, T0, 0)
380 total = total + 1
381 if sp_a == sp_b { pass = pass + 1; qz_ck("T4 deterministic" as *u8, 1) } else { qz_ck("T4 deterministic" as *u8, 0) }
382
383 qz_puts("---- nx_reader_quantize_gate: passed "); qz_pn(pass); qz_puts(" / "); qz_pn(total); qz_puts("\n" as *u8)
384 if pass == total { qz_puts("READER R4 GREEN -- our from-scratch reader runs 100% NO-FLOAT (Q16 integer) inference, FAITHFUL to the f32 model. Train-float / infer-int, complete + sovereign.\n" as *u8); return 0 }
385 qz_puts("RED -- R4 quantize not fully passed (see numbers)\n" as *u8)
386 return 1
387}