code wiki / _hdl_build / nx_vcodec_neural_entropy_gate.nx
nx_vcodec_neural_entropy_gate.nx source
↩ module page · 300 lines · 15999 B
1import "nx_gate_base.nx"
2// nx_vcodec_neural_entropy_gate.nx -- THE NEURAL ENTROPY RUNG, measured. Trains a probability model on real
3// coefficient significance and measures its held-out CROSS-ENTROPY (bits) vs the hand-designed context
4// buckets the sig-map coder uses. The range coder already takes an arbitrary probability per bit, so a
5// trained model that predicts P(significant | context) is a drop-in -- this gate proves whether TRAINING
6// beats HAND-RULES (the core neural-codec thesis: learned entropy models beat engineered ones). If the
7// trained model has lower held-out cross-entropy, that fraction of the measured 37% sig-map prize is
8// REALIZABLE by a sovereign integer entropy model. Model: logistic regression p=sigmoid(w.x), analytical
9// BCE gradient (p-y)x, batch GD -- fast, provably trains. Features are causal (available to enc AND dec).
10// GREEN = trained < hand-context < order-0 on HELD-OUT data (learned beats engineered beats none).
11// license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_dct8.nx"
14import "nx_vcodec.nx"
15import "nx_f32.nx"
16import "nx_f32_exp.nx"
17import "nx_f32_log.nx"
18import "nx_f32_div.nx"
19import "nx_f32_cvt.nx"
20
21const NW: i64 = 576
22const NH: i64 = 1024
23const NFEAT: i64 = 6 // bias, band, left-sig, up-sig, upleft-sig, neighbor-mag
24const MAXSAMP: i64 = 400000 // coefficient samples cap
25
26func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
27" as *u8); return ok }
28func gn(v: i64) -> i64 {
29 let b: *u8=sys_mmap(28); var m: i64=v; if m<0{sys_write(1,"-" as *u8,1);m=0-m}
30 let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}
31 var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
32func g3(v: i64) -> i64 { gn(v/1000); gw("." as *u8); let f: i64=v%1000; if f<100 { gw("0" as *u8) } if f<10 { gw("0" as *u8) } gn(f); return 0 }
33
34// ---- f32 helpers (ops take/return raw i64 bit patterns) ----
35let F_ZERO: i64 = 0
36func f_one() -> i64 { return nx_i32_to_f32(1) }
37func f_i(v: i64) -> i64 { return nx_i32_to_f32(v) }
38func f_frac(num: i64, den: i64) -> i64 { return nx_f32_div(nx_i32_to_f32(num), nx_i32_to_f32(den)) }
39// f32 -> i64 truncate toward zero (IEEE-754 decode)
40func f_to_i(raw: i64) -> i64 {
41 let sgn: i64 = (raw >> 31) & 1
42 let e: i64 = ((raw >> 23) & 0xff) - 127
43 if e < 0 { return 0 }
44 let mant: i64 = (raw & 0x7fffff) | 0x800000
45 var v: i64 = 0
46 if e >= 23 { let sh: i64 = e - 23; v = mant << sh } else { let sh2: i64 = 23 - e; v = mant >> sh2 }
47 if sgn == 1 { return 0 - v }
48 return v }
49// sigmoid(x) = 1/(1+exp(-x))
50func f_sigmoid(x: i64) -> i64 { return nx_f32_div(f_one(), nx_f32_add(f_one(), nx_f32_exp(nx_f32_neg(x)))) }
51// -log2(p) in milli-bits (i64): -log(p)/log(2) * 1000
52func f_neglog2_milli(p: i64) -> i64 {
53 var pp: i64 = p
54 let tiny: i64 = f_frac(1, 100000) // clamp p away from 0 (avoid -inf)
55 if nx_f32_lt(pp, tiny) == (1 as nx_int) { pp = tiny }
56 let ln2: i64 = nx_f32_log(f_i(2))
57 let nl: i64 = nx_f32_div(nx_f32_neg(nx_f32_log(pp)), ln2) // -log2(p)
58 return f_to_i(nx_f32_mul(nl, f_i(1000)))
59}
60
61// build coefficient samples: features (f32, NFEAT each) + label (0/1) from real frame-diff 8x8 DCT+quant.
62// returns sample count. feats[s*NFEAT+j], labels[s]. sig-map raster order (causal neighbors within block).
63func build_samples(yuv: *u8, f0: i64, f1: i64, qp: i64, M: *i64, scr: *i64, ti: *i64, to: *i64,
64 blk: *i64, sig: *i64, feats: *i64, labels: *i64) -> i64 {
65 let N: i64=NW*NH; let FB: i64=N+N/2
66 var ns: i64 = 0
67 var f: i64 = f0
68 while f <= f1 {
69 let cur: *u8 = ((yuv as i64) + f*FB) as *u8
70 let prv: *u8 = ((yuv as i64) + (f-1)*FB) as *u8
71 var by: i64 = 0
72 while by < NH/8 { var bx: i64 = 0
73 while bx < NW/8 { if ns + 64 <= MAXSAMP {
74 var yy: i64=0
75 while yy < 8 { var xx: i64=0
76 while xx < 8 { let p: i64=(by*8+yy)*NW+(bx*8+xx); blk[yy*8+xx]=(cur[p]&0xff)-(prv[p]&0xff); xx=xx+1 } yy=yy+1 }
77 nx_dct8_forward_2d(M, blk, blk, scr, ti, to)
78 vc_quant8(blk, qp)
79 var i: i64=0; while i<64 { if blk[i]!=0 { sig[i]=1 } else { sig[i]=0 } i=i+1 }
80 // raster scan; causal neighbors L(-1), U(-8), UL(-9)
81 var yb: i64=0
82 while yb < 8 { var xb: i64=0
83 while xb < 8 {
84 let pos: i64 = yb*8+xb
85 var ls: i64=0; var us: i64=0; var uls: i64=0; var lm: i64=0; var um: i64=0
86 if xb>0 { ls=sig[pos-1]; let lv: i64=blk[pos-1]; if lv<0 {lm=0-lv} else {lm=lv} }
87 if yb>0 { us=sig[pos-8]; let uv: i64=blk[pos-8]; if uv<0 {um=0-uv} else {um=uv} }
88 if xb>0 { if yb>0 { uls=sig[pos-9] } }
89 let base: i64 = ns*NFEAT
90 feats[base+0]=f_one() // bias
91 feats[base+1]=f_frac(pos, 63) // position band [0,1]
92 feats[base+2]=f_i(ls)
93 feats[base+3]=f_i(us)
94 feats[base+4]=f_i(uls)
95 var mm: i64 = lm+um; if mm>8 { mm=8 } // neighbor mag, clamped
96 feats[base+5]=f_frac(mm, 8)
97 labels[ns]=sig[pos]
98 ns=ns+1
99 xb=xb+1 } yb=yb+1 }
100 } bx=bx+1 } by=by+1 }
101 f=f+1
102 }
103 return ns }
104
105// held-out cross-entropy in milli-bits for the trained logistic model over samples [0,ns)
106func eval_model(w: *i64, feats: *i64, labels: *i64, ns: i64) -> i64 {
107 var bits: i64 = 0
108 var s: i64 = 0
109 while s < ns {
110 let base: i64 = s*NFEAT
111 var z: i64 = F_ZERO; var j: i64=0
112 while j < NFEAT { z = nx_f32_add(z, nx_f32_mul(w[j], feats[base+j])); j=j+1 }
113 let p: i64 = f_sigmoid(z)
114 if labels[s]==1 { bits = bits + f_neglog2_milli(p) }
115 else { bits = bits + f_neglog2_milli(nx_f32_sub(f_one(), p)) }
116 s=s+1
117 }
118 return bits }
119
120func main() -> i64 {
121 gw("=== nx_vcodec_neural_entropy_gate: TRAINED sig model vs hand-context (held-out cross-entropy) ===\n" as *u8)
122 let box: *i64 = sys_mmap(16) as *i64
123 let yuv: *u8 = sys_read_file("/mnt/c/Users/elder/nishi-core/nxc2/knowledge/staging/media/bframe_test_decoded.yuv" as *u8, box)
124 if (yuv as i64) == 0 { gw("cannot read yuv -> RED\n" as *u8); return 1 }
125 if box[0] < 12*(NW*NH+NW*NH/2) { gw("file too small -> RED\n" as *u8); return 1 }
126 let M: *i64 = sys_mmap(64*8) as *i64; nx_dct8_init(M)
127 let scr: *i64 = sys_mmap(64*8) as *i64; let ti: *i64 = sys_mmap(8*8) as *i64; let to: *i64 = sys_mmap(8*8) as *i64
128 let blk: *i64 = sys_mmap(64*8) as *i64; let sig: *i64 = sys_mmap(64*8) as *i64
129 let qp: i64 = 28
130
131 // TRAIN samples (frames 6..9) and TEST samples (10..11), separate arenas
132 let trF: *i64 = sys_mmap(MAXSAMP*NFEAT*8) as *i64; let trL: *i64 = sys_mmap(MAXSAMP*8) as *i64
133 let teF: *i64 = sys_mmap(MAXSAMP*NFEAT*8) as *i64; let teL: *i64 = sys_mmap(MAXSAMP*8) as *i64
134 let ntr: i64 = build_samples(yuv, 6, 9, qp, M, scr, ti, to, blk, sig, trF, trL)
135 let nte: i64 = build_samples(yuv, 10, 11, qp, M, scr, ti, to, blk, sig, teF, teL)
136 gw(" train samples=" as *u8); gn(ntr); gw(" test samples=" as *u8); gn(nte); gw("\n" as *u8)
137
138 // ---- TRAIN logistic regression: batch GD, analytical BCE gradient (p-y)*x ----
139 // Init the BIAS to the base-rate logit ln(pos/neg) so training STARTS at order-0 calibration and can
140 // only improve (a diverging-from-zero start with high lr produced confidently-wrong predictions worse
141 // than no-context). Low lr = stable batch GD on 400k imbalanced samples.
142 let w: *i64 = sys_mmap(NFEAT*8) as *i64
143 let g: *i64 = sys_mmap(NFEAT*8) as *i64
144 var j: i64=0; while j<NFEAT { w[j]=F_ZERO; j=j+1 }
145 var bpos: i64=0; var s0: i64=0; while s0<ntr { if trL[s0]==1 { bpos=bpos+1 } s0=s0+1 }
146 let bneg: i64 = ntr - bpos
147 if bpos > 0 { if bneg > 0 { w[0] = nx_f32_log(f_frac(bpos, bneg)) } } // bias = logit(base rate)
148 let lr: i64 = f_frac(5, 100) // learning rate 0.05
149 let invn: i64 = nx_f32_div(f_one(), f_i(ntr))
150 var ep: i64=0
151 while ep < 120 {
152 j=0; while j<NFEAT { g[j]=F_ZERO; j=j+1 }
153 var s: i64=0
154 while s < ntr {
155 let base: i64 = s*NFEAT
156 var z: i64=F_ZERO; j=0
157 while j<NFEAT { z=nx_f32_add(z, nx_f32_mul(w[j], trF[base+j])); j=j+1 }
158 let p: i64 = f_sigmoid(z)
159 let err: i64 = nx_f32_sub(p, f_i(trL[s])) // p - y
160 j=0; while j<NFEAT { g[j]=nx_f32_add(g[j], nx_f32_mul(err, trF[base+j])); j=j+1 }
161 s=s+1
162 }
163 j=0; while j<NFEAT { w[j]=nx_f32_sub(w[j], nx_f32_mul(lr, nx_f32_mul(g[j], invn))); j=j+1 }
164 ep=ep+1
165 }
166
167 // ---- HAND-CONTEXT model: empirical P(sig) per (band4 x neighborcount4) bucket, learned from TRAIN ----
168 let hc1: *i64 = sys_mmap(16*8) as *i64 // count sig
169 let hc0: *i64 = sys_mmap(16*8) as *i64 // count total
170 var b: i64=0; while b<16 { hc1[b]=0; hc0[b]=0; b=b+1 }
171 // recompute bucket from features: band = round(feat1*3), nbcount = feat2+feat3+feat4 (0..3)
172 var s2: i64=0
173 while s2 < ntr {
174 let base: i64 = s2*NFEAT
175 let band: i64 = f_to_i(nx_f32_mul(trF[base+1], f_i(3))) // 0..3
176 let nb: i64 = f_to_i(trF[base+2]) + f_to_i(trF[base+3]) + f_to_i(trF[base+4]) // 0..3
177 var bk: i64 = band*4+nb; if bk<0 {bk=0} if bk>15 {bk=15}
178 hc0[bk]=hc0[bk]+1; if trL[s2]==1 { hc1[bk]=hc1[bk]+1 }
179 s2=s2+1
180 }
181 // order-0 global freq
182 var g1: i64=0; var g0: i64=0
183 s2=0; while s2<ntr { g0=g0+1; if trL[s2]==1 { g1=g1+1 } s2=s2+1 }
184
185 // ---- EVAL all three on HELD-OUT (milli-bits) ----
186 let bTrained: i64 = eval_model(w, teF, teL, nte)
187 var bHand: i64=0; var bOrd0: i64=0
188 let ordp: i64 = f_frac(g1, g0)
189 var s3: i64=0
190 while s3 < nte {
191 let base: i64 = s3*NFEAT
192 let band: i64 = f_to_i(nx_f32_mul(teF[base+1], f_i(3)))
193 let nb: i64 = f_to_i(teF[base+2]) + f_to_i(teF[base+3]) + f_to_i(teF[base+4])
194 var bk: i64 = band*4+nb; if bk<0 {bk=0} if bk>15 {bk=15}
195 var pb: i64 = f_frac(1,2); if hc0[bk]>0 { pb=f_frac(hc1[bk], hc0[bk]) }
196 if teL[s3]==1 { bHand=bHand+f_neglog2_milli(pb); bOrd0=bOrd0+f_neglog2_milli(ordp) }
197 else { bHand=bHand+f_neglog2_milli(nx_f32_sub(f_one(),pb)); bOrd0=bOrd0+f_neglog2_milli(nx_f32_sub(f_one(),ordp)) }
198 s3=s3+1
199 }
200
201 // ---- MLP (6 -> H ReLU -> 1 sigmoid): the NONLINEAR trained model -- the real test of whether a learned
202 // model can beat the hand-context BUCKETS (which are themselves a coarse nonlinear lookup). Batch GD,
203 // mixed-sign hidden-bias init (ReLU needs negative-bias units), lr 0.1 (the XOR-MLP recipe). Subsample
204 // train to 100k for speed; eval on the full held-out set. ----
205 let H: i64 = 8
206 let W1: *i64 = sys_mmap(H*NFEAT*8) as *i64; let b1: *i64 = sys_mmap(H*8) as *i64
207 let W2: *i64 = sys_mmap(H*8) as *i64; let b2p: *i64 = sys_mmap(8) as *i64
208 let gW1: *i64 = sys_mmap(H*NFEAT*8) as *i64; let gb1: *i64 = sys_mmap(H*8) as *i64
209 let gW2: *i64 = sys_mmap(H*8) as *i64; let gb2: *i64 = sys_mmap(8) as *i64
210 let hpre: *i64 = sys_mmap(H*8) as *i64; let hact: *i64 = sys_mmap(H*8) as *i64
211 var k: i64=0
212 while k < H {
213 var i2: i64=0; while i2 < NFEAT { W1[k*NFEAT+i2] = f_frac(((k*7+i2*13)%11)-5, 40); i2=i2+1 } // small deterministic spread
214 b1[k] = f_frac((k%3)-1, 4) // mixed-sign hidden bias {-0.25,0,0.25}
215 W2[k] = f_frac((k%5)-2, 20)
216 k=k+1
217 }
218 b2p[0] = F_ZERO; if bpos>0 { if bneg>0 { b2p[0]=nx_f32_log(f_frac(bpos,bneg)) } }
219 let lrM: i64 = f_frac(1,10)
220 let sub: i64 = ntr/100000; var subS: i64 = sub; if subS<1 { subS=1 }
221 var nsub: i64 = 0; var cc: i64=0; while cc<ntr { nsub=nsub+1; cc=cc+subS }
222 let invM: i64 = nx_f32_div(f_one(), f_i(nsub))
223 var em: i64=0
224 while em < 60 {
225 k=0; while k<H { var i3: i64=0; while i3<NFEAT { gW1[k*NFEAT+i3]=F_ZERO; i3=i3+1 } gb1[k]=F_ZERO; gW2[k]=F_ZERO; k=k+1 }
226 gb2[0]=F_ZERO
227 var s: i64=0
228 while s < ntr {
229 let base: i64 = s*NFEAT
230 // forward
231 var opre: i64 = b2p[0]
232 k=0
233 while k < H {
234 var pre: i64 = b1[k]; var i4: i64=0
235 while i4 < NFEAT { pre=nx_f32_add(pre, nx_f32_mul(W1[k*NFEAT+i4], trF[base+i4])); i4=i4+1 }
236 hpre[k]=pre
237 var a: i64 = pre; if nx_f32_lt(pre, F_ZERO)==(1 as nx_int) { a=F_ZERO } // relu
238 hact[k]=a
239 opre=nx_f32_add(opre, nx_f32_mul(W2[k], a))
240 k=k+1
241 }
242 let p: i64 = f_sigmoid(opre)
243 let dout: i64 = nx_f32_sub(p, f_i(trL[s])) // BCE+sigmoid combined
244 gb2[0]=nx_f32_add(gb2[0], dout)
245 k=0
246 while k < H {
247 gW2[k]=nx_f32_add(gW2[k], nx_f32_mul(dout, hact[k]))
248 var dh: i64 = nx_f32_mul(dout, W2[k])
249 if nx_f32_lt(hpre[k], F_ZERO)==(1 as nx_int) { dh=F_ZERO } // relu'
250 gb1[k]=nx_f32_add(gb1[k], dh)
251 var i5: i64=0; while i5<NFEAT { gW1[k*NFEAT+i5]=nx_f32_add(gW1[k*NFEAT+i5], nx_f32_mul(dh, trF[base+i5])); i5=i5+1 }
252 k=k+1
253 }
254 s=s+subS
255 }
256 // update
257 k=0
258 while k < H {
259 var i6: i64=0; while i6<NFEAT { W1[k*NFEAT+i6]=nx_f32_sub(W1[k*NFEAT+i6], nx_f32_mul(lrM, nx_f32_mul(gW1[k*NFEAT+i6], invM))); i6=i6+1 }
260 b1[k]=nx_f32_sub(b1[k], nx_f32_mul(lrM, nx_f32_mul(gb1[k], invM)))
261 W2[k]=nx_f32_sub(W2[k], nx_f32_mul(lrM, nx_f32_mul(gW2[k], invM)))
262 k=k+1
263 }
264 b2p[0]=nx_f32_sub(b2p[0], nx_f32_mul(lrM, nx_f32_mul(gb2[0], invM)))
265 em=em+1
266 }
267 // eval MLP on held-out
268 var bMLP: i64=0
269 var sm: i64=0
270 while sm < nte {
271 let base: i64 = sm*NFEAT
272 var opre: i64=b2p[0]
273 k=0
274 while k<H {
275 var pre: i64=b1[k]; var i7: i64=0
276 while i7<NFEAT { pre=nx_f32_add(pre, nx_f32_mul(W1[k*NFEAT+i7], teF[base+i7])); i7=i7+1 }
277 var a: i64=pre; if nx_f32_lt(pre,F_ZERO)==(1 as nx_int) { a=F_ZERO }
278 opre=nx_f32_add(opre, nx_f32_mul(W2[k], a))
279 k=k+1
280 }
281 let p: i64=f_sigmoid(opre)
282 if teL[sm]==1 { bMLP=bMLP+f_neglog2_milli(p) } else { bMLP=bMLP+f_neglog2_milli(nx_f32_sub(f_one(),p)) }
283 sm=sm+1
284 }
285
286 gw(" held-out cross-entropy (bits): order0=" as *u8); g3(bOrd0/1000)
287 gw(" hand-context=" as *u8); g3(bHand/1000)
288 gw(" logistic=" as *u8); g3(bTrained/1000)
289 gw(" MLP=" as *u8); g3(bMLP/1000); gw("\n" as *u8)
290 let impH: i64 = (bHand-bMLP)*1000/bHand
291 gw(" hand beats order0 by " as *u8); gn((bOrd0-bHand)*1000/bOrd0)
292 gw("permille; MLP beats hand by " as *u8); gn(impH); gw("permille\n" as *u8)
293
294 var pass: i64=0; var tot: i64=0
295 tot=tot+1; if bHand < bOrd0 { pass=pass+1 } // hand-rules beat no-context (sanity)
296 tot=tot+1; if bMLP < bHand { pass=pass+1 } // the TRAINED NONLINEAR model beats hand-rules (the neural win)
297 tot=tot+1; if nte > 50000 { pass=pass+1 } // measured on real substantial held-out data
298 gw("NEURAL-ENTROPY: " as *u8); gn(pass); gw("/" as *u8); gn(tot)
299 if pass==tot { gw(" GREEN -- a TRAINED NONLINEAR entropy model beats hand-rules on held-out data (neural rung REAL)\n" as *u8); return 0 }
300 gw(" RED -- trained model does NOT beat hand-context (entropy-on-DCT near ceiling; SOTA needs learned representation)\n" as *u8); return 1 }