code wiki / _hdl_build / nx_nn_train_gate.nx
nx_nn_train_gate.nx source
↩ module page · 52 lines · 3108 B
1// nx_nn_train_gate.nx -- proves sovereign fixed-point training (nx_nn_train): batch gradient descent on a linear layer,
2// starting from zero weights, learns a known target mapping -- the MSE loss collapses and the learned weights converge
3// to the target. This is the training pipeline the neural PLC / RVQ-codebook needs, all integer, no FPU/GPU/autograd.
4import "nx_syscalls.nx"
5import "nx_gate_emit_lib.nx"
6import "nx_nn_train.nx"
7import "nx_gate_verdict.nx"
8
9func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v }
10
11func main() -> i64 {
12 g_puts("nx_nn_train gate (sovereign fixed-point gradient descent learns a target, MEASURED)\n" as *u8)
13 var pass: i64 = 0; var total: i64 = 0
14 let I: i64 = 2; let O: i64 = 1; let T: i64 = 4; let Q: i64 = 16 // weights in Q16 so sub-unit GD steps accumulate
15
16 // target mapping W* = [1.0, -0.5] in Q16
17 let Wt: *i64 = sys_mmap(2*8) as *i64; Wt[0]=65536; Wt[1]=0-32768
18 // training inputs X[T x I] (Q8) and targets Y = (W* . X) >> Q
19 let X: *i64 = sys_mmap(T*I*8) as *i64
20 let Y: *i64 = sys_mmap(T*O*8) as *i64
21 X[0]=256; X[1]=256; X[2]=512; X[3]=128; X[4]=128; X[5]=512; X[6]=384; X[7]=256
22 var t: i64 = 0
23 while t < T { let acc: i64 = Wt[0]*X[t*I] + Wt[1]*X[t*I+1]; Y[t] = acc >> Q; t = t + 1 }
24
25 // learn from ZERO weights
26 let W: *i64 = sys_mmap(2*8) as *i64; W[0]=0; W[1]=0
27 let grad: *i64 = sys_mmap(O*I*8) as *i64
28 let pred: *i64 = sys_mmap(O*8) as *i64
29
30 let loss0: i64 = nnt_loss(X, Y, T, W, I, O, Q)
31 var it: i64 = 0
32 while it < 15000 { nnt_step(X, Y, T, W, I, O, Q, 13, grad, pred); it = it + 1 } // update_shift 13 = the learning rate
33 let lossN: i64 = nnt_loss(X, Y, T, W, I, O, Q)
34
35 g_puts(" [measure] loss: start=" as *u8); g_pn(loss0); g_puts(" after 15000 GD steps=" as *u8); g_pn(lossN)
36 g_puts(" learned W=[" as *u8); g_pn(W[0]); g_puts("," as *u8); g_pn(W[1]); g_puts("] vs target [65536,-32768] (Q16; exact convergence is fixed-point-floor-limited)\n" as *u8)
37
38 pass = pass + g_check("training collapses the loss (>= 95% reduction = it learned the mapping)" as *u8, lossN * 20 <= loss0); total=total+1
39 pass = pass + g_check("weights converge substantially toward target with correct sign (>= 60% of the way)" as *u8, (W[0] >= 39322) & (W[1] <= 0-19661)); total=total+1
40 pass = pass + g_check("started genuinely untrained (start loss > 0)" as *u8, loss0 > 0); total=total+1
41
42 g_puts("---- nn_train gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8)
43 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check
44 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled
45 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify.
46 let ctr__dry: *i64 = gv_ctr()
47 ctr__dry[0] = pass
48 ctr__dry[1] = total
49 let rc__dry: i64 = gv_verdict("NN-TRAIN-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8)
50 sys_exit(rc__dry)
51 return rc__dry
52}