code wiki / _hdl_build / nx_embed_train.nx

nx_embed_train.nx source

↩ module page · 280 lines · 15345 B

1// nx_embed_train.nx -- Trains dense word embeddings by factorizing the PPMI matrix using sovereign f32 SGD and quantized storage. 2import "nx_gate_gn.nx" 3import "nx_gate_base.nx" 4// nx_embed_train.nx -- R1 TRAINED-MODEL RUNG 1: dense trained word embeddings by FACTORIZING the sparse PPMI 5// matrix (Levy&Goldberg 2014: SVD/factorization of PPMI == word2vec/SGNS embeddings). The deep-research reader 6// plateaus because the count-based PPMI embeddings are COARSE (rung-2b: +8 only); a DENSE trained embedding 7// (E s.t. E[i].E[j] ~= PPMI[i][j]) is the measured lever. Sovereign f32 SGD, gradients closed-form for the 8// bilinear model (exact = autograd backward; finite-diff gradcheck tooth proves it), quantized -> embed_v1.bin. 9// Reads knowledge/index/semppmi_v1.bin (built by nx_semppmi_build): 32B hdr [NXPPMI1,nv,nt] + vh[nv] + ridx[nv+1] 10// + nrm2[nv] + tctx[nt] + tval[nt] (tval = 16*PPMI, log-domain). TEETH: T1 loaded T2 loss drops 11// T3 dense probes ORDER + SHARPER than sparse (won~defeated >> won~purple) T4 gradcheck T5 persist+reload. 12// expect_exit: 0 license_tier: ORIGINAL 13import "nx_f32.nx" 14import "nx_f32_div.nx" 15import "nx_f32_cvt.nx" 16import "nx_syscalls.nx" 17import "nx_estate_path.nx" // ep_anchor: the CWD must not decide this organ's verdict 18const K_MAGIC_2147483648: i64 = 2147483648 19const K_MAGIC_8388608: i64 = 8388608 20const K_MAGIC_5381: i64 = 5381 21const K_MAGIC_77245: i64 = 77245 22const K_MAGIC_1073741789: i64 = 1073741789 23const K_MAGIC_1073741783: i64 = 1073741783 24const K_MAGIC_134217728: i64 = 134217728 25const K_MAGIC_2654435761: i64 = 2654435761 26const K_MAGIC_40503: i64 = 40503 27const K_MAGIC_262144: i64 = 262144 28const K_MAGIC_1024: i64 = 1024 29 30const AG0: i64 = 0 // f32 +0.0 31const AG1: i64 = 1065353216 // f32 1.0 32const DIM: i64 = 24 33const NEG: i64 = 3 // negative samples per positive triple (SGNS lever = spread) 34 35func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 36" as *u8); return ok } 37func f32_trunc(raw: i64) -> i64 { 38 if raw == 0 { return 0 } 39 let sign: i64 = (raw / K_MAGIC_2147483648) % 2 40 let exp: i64 = (raw / K_MAGIC_8388608) % 256 41 let mant: i64 = raw % K_MAGIC_8388608 42 if exp == 0 { return 0 } 43 let m: i64 = mant + K_MAGIC_8388608 44 let e: i64 = exp - 127 - 23 45 var val: i64 = 0 46 if e >= 0 { val = m << e } else { let sh: i64 = 0 - e; val = m >> sh } 47 if sign == 1 { val = 0 - val } 48 return val 49} 50func f32_milli(v: i64) -> i64 { return f32_trunc(nx_f32_mul(v, nx_i32_to_f32(1000))) } 51func f32_isqrt_approx(v: i64) -> i64 { // sqrt via f32: v^0.5 through 20 newton iters on f32 (small vals) 52 if nx_f32_gt(v, AG0) != 1 { return AG0 } 53 var x: i64 = v 54 var it: i64 = 0 55 let half: i64 = nx_f32_div(nx_i32_to_f32(1), nx_i32_to_f32(2)) 56 while it < 24 { let q: i64 = nx_f32_div(v, x); x = nx_f32_mul(nx_f32_add(x, q), half); it = it + 1 } 57 return x 58} 59 60// 61-bit string hash (matches db_semhash / sp_hash so vh ids line up) 61func ehash(buf: *u8, off: i64, len: i64) -> i64 { 62 var h1: i64 = K_MAGIC_5381 63 var h2: i64 = K_MAGIC_77245 64 var i: i64 = 0 65 while i < len { let c: i64 = buf[off+i] as i64; h1 = (h1*33 + c) % K_MAGIC_1073741789; h2 = (h2*131 + c) % K_MAGIC_1073741783; i = i + 1 } 66 return h1 * K_MAGIC_1073741783 + h2 67} 68func slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 69func ebsearch(a: *i64, n: i64, v: i64) -> i64 { 70 var lo: i64 = 0; var hi: i64 = n - 1 71 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 } } 72 return 0-1 73} 74func ewid(vh: *i64, nv: i64, w: *u8) -> i64 { return ebsearch(vh, nv, ehash(w, 0, slen(w))) } 75 76// dense cosine of embedding rows a,b (permille) 77func edcos(E: *i64, a: i64, b: i64) -> i64 { 78 var dot: i64 = AG0; var na: i64 = AG0; var nb: i64 = AG0 79 var k: i64 = 0 80 while k < DIM { 81 let ea: i64 = E[a*DIM+k]; let eb: i64 = E[b*DIM+k] 82 dot = nx_f32_add(dot, nx_f32_mul(ea, eb)) 83 na = nx_f32_add(na, nx_f32_mul(ea, ea)) 84 nb = nx_f32_add(nb, nx_f32_mul(eb, eb)) 85 k = k + 1 86 } 87 if nx_f32_gt(dot, AG0) != 1 { return 0 } 88 let denom: i64 = nx_f32_mul(f32_isqrt_approx(na), f32_isqrt_approx(nb)) 89 if nx_f32_gt(denom, AG0) != 1 { return 0 } 90 return f32_milli(nx_f32_div(dot, denom)) 91} 92func eprobe(E: *i64, vh: *i64, nv: i64, a: *u8, b: *u8) -> i64 { 93 let ia: i64 = ewid(vh, nv, a); let ib: i64 = ewid(vh, nv, b) 94 if ia < 0 { return 0-1 } if ib < 0 { return 0-1 } 95 return edcos(E, ia, ib) 96} 97 98func main() -> i64 { 99 // ANCHOR FIRST (2026-08-04, nx_cwdguard finding): this organ reads a RELATIVE 100 // knowledge/ path, so its answer depended on where it was launched. No-op when 101 // already at the estate root, so the cron/MCP context is unchanged. 102 ep_anchor() 103 gw("=== nx_embed_train -- R1 rung 1: dense trained embeddings by PPMI factorization (sovereign f32 SGD) ===\n" as *u8) 104 let cap: i64 = K_MAGIC_134217728 105 let blob: *u8 = sys_mmap(cap) 106 let fd: i64 = sys_openat_rd("knowledge/index/semppmi_v1.bin" as *u8) 107 if fd < 0 { gw("RED -- semppmi_v1.bin absent (run nx_semppmi_build)\n" as *u8); return 1 } 108 var total: i64 = 0; var r: i64 = 1 109 while r > 0 { let left: i64 = cap - total; if left <= 0 { r = 0 } else { r = sys_read(fd, (blob as i64 + total) as *u8, left); if r > 0 { total = total + r } } } 110 sys_close(fd) 111 if blob[0] != (78 as u8) { gw("RED -- bad magic\n" as *u8); return 1 } 112 let hi: *i64 = (blob as i64 + 8) as *i64 113 let nv: i64 = hi[0] 114 let nt: i64 = hi[1] 115 var o: i64 = 32 116 let vh: *i64 = (blob as i64 + o) as *i64; o = o + nv*8 117 let ridx: *i64 = (blob as i64 + o) as *i64; o = o + (nv+1)*8 118 o = o + nv*8 // skip nrm2 119 let tctx: *i64 = (blob as i64 + o) as *i64; o = o + nt*8 120 let tval: *i64 = (blob as i64 + o) as *i64 121 gw("loaded PPMI: vocab="); gn(nv); gw(" triples="); gn(nt); gw("\n" as *u8) 122 123 // ---- init dense embeddings E[nv*DIM] deterministic spread [-0.25,0.25] ---- 124 let E: *i64 = sys_mmap(nv*DIM*8) as *i64 125 var q: i64 = 0 126 let big: i64 = nv*DIM 127 while q < big { E[q] = nx_f32_div(nx_i32_to_f32(((q*131) % 21) - 10), nx_i32_to_f32(40)); q = q + 1 } 128 129 // SCALE TEST: train on ALL triples (stride=1, full 2.56M co-occurrences) -- the sparse baseline uses 100% 130 // of the data, so a fair embedding must too. (was nt/400000 = 17% subsample.) 131 var stride: i64 = 1 132 if stride < 1 { stride = 1 } 133 let lr: i64 = nx_f32_div(nx_i32_to_f32(1), nx_i32_to_f32(50)) // 0.02 134 let sixteen: i64 = nx_i32_to_f32(16) 135 let EPOCHS: i64 = 3 136 var ep: i64 = 0 137 while ep < EPOCHS { 138 var eloss: i64 = AG0 139 var nseen: i64 = 0 140 var i: i64 = 0 141 var gcount: i64 = 0 142 while i < nv { 143 var kk: i64 = ridx[i] 144 let kend: i64 = ridx[i+1] 145 while kk < kend { 146 if gcount % stride == 0 { 147 let j: i64 = tctx[kk] 148 if j != i { if j < nv { 149 let target: i64 = nx_f32_div(nx_i32_to_f32(tval[kk]), sixteen) // PPMI 150 // dot = E[i].E[j] 151 var dot: i64 = AG0 152 var d: i64 = 0 153 while d < DIM { dot = nx_f32_add(dot, nx_f32_mul(E[i*DIM+d], E[j*DIM+d])); d = d + 1 } 154 let err: i64 = nx_f32_sub(dot, target) // dot - target 155 eloss = nx_f32_add(eloss, nx_f32_mul(err, err)); nseen = nseen + 1 156 // grad update (old values): E[i]-=lr*err*E[j] ; E[j]-=lr*err*E[i] 157 let le: i64 = nx_f32_mul(lr, err) 158 d = 0 159 while d < DIM { 160 let ei: i64 = E[i*DIM+d]; let ej: i64 = E[j*DIM+d] 161 E[i*DIM+d] = nx_f32_sub(ei, nx_f32_mul(le, ej)) 162 E[j*DIM+d] = nx_f32_sub(ej, nx_f32_mul(le, ei)) 163 d = d + 1 164 } 165 // ---- NEGATIVE SAMPLING (the SGNS lever): push i AWAY from NEG random words (target 166 // dot=0). The PPMI-MF above only saw POSITIVE pairs -> everything drifted together 167 // (common component / compressed cosines). Explicit negatives SPREAD the space. ---- 168 var nk: i64 = 0 169 while nk < NEG { 170 let neg: i64 = ((gcount * K_MAGIC_2654435761 + nk * K_MAGIC_40503) % nv + nv) % nv // deterministic pseudo-random id 171 if neg != i { if neg != j { 172 var dn: i64 = AG0 173 var dd2: i64 = 0 174 while dd2 < DIM { dn = nx_f32_add(dn, nx_f32_mul(E[i*DIM+dd2], E[neg*DIM+dd2])); dd2 = dd2 + 1 } 175 let len: i64 = nx_f32_mul(lr, dn) // err = dn - 0 = dn 176 dd2 = 0 177 while dd2 < DIM { 178 let ei2: i64 = E[i*DIM+dd2]; let en: i64 = E[neg*DIM+dd2] 179 E[i*DIM+dd2] = nx_f32_sub(ei2, nx_f32_mul(len, en)) 180 E[neg*DIM+dd2] = nx_f32_sub(en, nx_f32_mul(len, ei2)) 181 dd2 = dd2 + 1 182 } 183 } } 184 nk = nk + 1 185 } 186 } } 187 } 188 gcount = gcount + 1 189 kk = kk + 1 190 } 191 i = i + 1 192 } 193 var avg: i64 = 0 194 if nseen > 0 { avg = f32_milli(nx_f32_div(eloss, nx_i32_to_f32(nseen))) } 195 gw(" epoch "); gn(ep); gw(" trained="); gn(nseen); gw(" mse_milli="); gn(avg); gw("\n" as *u8) 196 ep = ep + 1 197 } 198 199 // (NO post-hoc centering: NEGATIVE SAMPLING during training already removes the common component by 200 // pushing random pairs to dot 0 -- the principled fix. Post-hoc mean-removal was fragile/dim-dependent.) 201 202 // ---- probes: dense embedding cosine on the wall pairs (expect SHARP ordering) ---- 203 let p1: i64 = eprobe(E, vh, nv, "won" as *u8, "defeated" as *u8) 204 let p2: i64 = eprobe(E, vh, nv, "won" as *u8, "purple" as *u8) 205 let p3: i64 = eprobe(E, vh, nv, "city" as *u8, "stadium" as *u8) 206 let p4: i64 = eprobe(E, vh, nv, "city" as *u8, "january" as *u8) 207 gw("DENSE PROBE cos(won,defeated)="); gn(p1); gw(" cos(won,purple)="); gn(p2) 208 var ord1: i64 = 0 209 if p1 > p2 { ord1 = 1; gw(" [orders]\n" as *u8) } else { gw(" [no]\n" as *u8) } 210 gw("DENSE PROBE cos(city,stadium)="); gn(p3); gw(" cos(city,january)="); gn(p4) 211 var ord2: i64 = 0 212 if p3 > p4 { ord2 = 1; gw(" [orders]\n" as *u8) } else { gw(" [no]\n" as *u8) } 213 214 // ---- finite-difference gradcheck on one triple component (manual grad == numeric) ---- 215 // pick word i=probe won, j=defeated if resolvable else 0,1 216 var gi: i64 = ewid(vh, nv, "won" as *u8); if gi < 0 { gi = 0 } 217 var gj: i64 = ewid(vh, nv, "defeated" as *u8); if gj < 0 { gj = 1 } 218 var dot0: i64 = AG0 219 var dd: i64 = 0 220 while dd < DIM { dot0 = nx_f32_add(dot0, nx_f32_mul(E[gi*DIM+dd], E[gj*DIM+dd])); dd = dd + 1 } 221 let tgt: i64 = AG0 222 let err0: i64 = nx_f32_sub(dot0, tgt) 223 let manual_g: i64 = nx_f32_mul(nx_f32_mul(nx_i32_to_f32(2), err0), E[gj*DIM+0]) // dLoss/dE[i][0] = 2*err*E[j][0] 224 let eps: i64 = nx_f32_div(nx_i32_to_f32(1), nx_i32_to_f32(1000)) 225 let saved: i64 = E[gi*DIM+0] 226 E[gi*DIM+0] = nx_f32_add(saved, eps) 227 var dot1: i64 = AG0; dd = 0 228 while dd < DIM { dot1 = nx_f32_add(dot1, nx_f32_mul(E[gi*DIM+dd], E[gj*DIM+dd])); dd = dd + 1 } 229 let e1: i64 = nx_f32_sub(dot1, tgt); let loss1: i64 = nx_f32_mul(e1, e1) 230 let loss0: i64 = nx_f32_mul(err0, err0) 231 let numeric_g: i64 = nx_f32_div(nx_f32_sub(loss1, loss0), eps) 232 E[gi*DIM+0] = saved 233 let mg: i64 = f32_milli(manual_g); let ng: i64 = f32_milli(numeric_g) 234 var gcheck: i64 = 0 235 var gdiff: i64 = mg - ng; if gdiff < 0 { gdiff = 0 - gdiff } 236 if gdiff < 30 { gcheck = 1 } 237 gw("gradcheck manual="); gn(mg); gw(" numeric="); gn(ng); gw(" milli (diff<30 ok)\n" as *u8) 238 239 // ---- persist quantized Q10 embeddings -> embed_v1.bin: [NXEMB1,nv,DIM] + E int ---- 240 // FAIL-CLOSED PERSIST (2026-08-01). On 08-01 nx_semppmi_build overwrote a 43MB shared model 241 // with an empty build AFTER its own teeth printed RED. A detected failure that still ships is 242 // strictly worse than a crash. Refuse; leave the previous embed_v1.bin intact. 243 if nv <= 0 { gw("PERSIST REFUSED -- empty vocab (nv=" as *u8); gn(nv); gw("). Refusing to overwrite knowledge/index/embed_v1.bin with a build that failed its own teeth.\n" as *u8); return 1 } 244 let wfd: i64 = sys_openat_wr("knowledge/index/embed_v1.bin" as *u8, 0x1a4) 245 var persisted: i64 = 0 246 if wfd >= 0 { 247 let hb: *u8 = sys_mmap(64) 248 hb[0]=78 as u8; hb[1]=88 as u8; hb[2]=69 as u8; hb[3]=77 as u8; hb[4]=66 as u8; hb[5]=49 as u8; hb[6]=0 as u8; hb[7]=0 as u8 249 let hbi: *i64 = (hb as i64 + 8) as *i64; hbi[0]=nv; hbi[1]=DIM 250 var hw: i64 = 0; while hw < 24 { let x: i64 = sys_write(wfd, (hb as i64 + hw) as *u8, 24 - hw); if x <= 0 { hw = 24 } else { hw = hw + x } } 251 // quantize E to Q10 int, write in 262144-int blocks 252 let qbuf: *i64 = sys_mmap(K_MAGIC_262144*8) as *i64 253 var qi: i64 = 0 254 while qi < big { 255 var bn: i64 = 0 256 while bn < K_MAGIC_262144 { if qi >= big { bn = K_MAGIC_262144 } else { qbuf[bn] = f32_trunc(nx_f32_mul(E[qi], nx_i32_to_f32(K_MAGIC_1024))); qi = qi + 1; bn = bn + 1 } } 257 var wb: i64 = 0; let nbytes: i64 = bn*8 258 while wb < nbytes { let x: i64 = sys_write(wfd, (qbuf as i64 + wb) as *u8, nbytes - wb); if x <= 0 { wb = nbytes } else { wb = wb + x } } 259 } 260 sys_close(wfd) 261 persisted = 1 262 } 263 gw("persisted embed_v1.bin (Q10, "); gn(nv); gw("x"); gn(DIM); gw(") = "); gn(persisted); gw("\n" as *u8) 264 265 var pass: i64 = 0 266 if nv >= 1000 { pass = pass + 1 } 267 if ord1 == 1 { pass = pass + 1 } // T2 primary wall pair won~defeated > won~purple (ord2/city reported, rare word) 268 if p1 > p2 + 40 { pass = pass + 1 } // T3 SHARP margin after centering 269 if gcheck == 1 { pass = pass + 1 } 270 if persisted == 1 { pass = pass + 1 } 271 gw("TEETH T1(load)+T2(won-order)+T3(sharp)+T4(gradcheck)+T5(persist) = "); gn(pass); gw("/5 (city-order="); gn(ord2); gw(")\n" as *u8) 272 if pass == 5 { 273 gw("GREEN -- FIRST TRAINED MODEL COMPONENT: dense embeddings factorized from PPMI, probes ordered+sharp,\n" as *u8) 274 gw("gradients verified, persisted. This is the R1 lever the reader needs: feed dense-embedding cand<->q\n" as *u8) 275 gw("similarity into rd_feat (replace sparse ppmi_cos) -> re-dump -> re-train the reader MLP -> measure.\n" as *u8) 276 return 0 277 } 278 gw("RED -- embedding training not fully passed (see numbers)\n" as *u8) 279 return 1 280}