code wiki / _hdl_build / _autograd_authored.nx
_autograd_authored.nx source
↩ module page · 195 lines · 8516 B
1// AUTHORED BY THE NISHI BUILDER (nx_module_author autograd template) -- L1 training substrate rung 1.
2// Tape reverse-mode autograd on the sovereign f32 tower; tutoring: specs/2026-06-09-tutoring-training-substrate-rung1.md
3import "nx_syscalls.nx"
4import "nx_f32.nx"
5import "nx_f32_div.nx"
6import "nx_f32_cvt.nx"
7func _ag_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
8func _ag_num(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
9func ag_q(n: i64, d: i64) -> i64 { return nx_f32_div(nx_i32_to_f32(n), nx_i32_to_f32(d)) }
10func ag_milli(v: i64) -> i64 {
11 var neg: i64 = 0
12 var a: i64 = v
13 if nx_f32_lt(a, 0) == 1 { neg = 1; a = nx_f32_abs(a) }
14 let m: i64 = nx_f32_mul(a, nx_i32_to_f32(1000))
15 var lo: i64 = 0
16 var hi: i64 = 16777216
17 while lo < hi {
18 let mid: i64 = (lo + hi + 1) / 2
19 if nx_f32_lt(m, nx_i32_to_f32(mid)) == 1 { hi = mid - 1 } else { lo = mid }
20 }
21 if neg == 1 { return 0 - lo }
22 return lo
23}
24func ag_node(tape: *i64, nb: *i64, op: i64, ai: i64, bi: i64, v: i64) -> i64 {
25 let r: i64 = nb[0]
26 tape[r*5+0] = op
27 tape[r*5+1] = ai
28 tape[r*5+2] = bi
29 tape[r*5+3] = v
30 tape[r*5+4] = 0
31 nb[0] = r + 1
32 return r
33}
34func ag_leaf(tape: *i64, nb: *i64, v: i64) -> i64 { return ag_node(tape, nb, 0, 0, 0, v) }
35func ag_add(tape: *i64, nb: *i64, x: i64, y: i64) -> i64 { return ag_node(tape, nb, 1, x, y, nx_f32_add(tape[x*5+3], tape[y*5+3])) }
36func ag_mul(tape: *i64, nb: *i64, x: i64, y: i64) -> i64 { return ag_node(tape, nb, 2, x, y, nx_f32_mul(tape[x*5+3], tape[y*5+3])) }
37func ag_sub(tape: *i64, nb: *i64, x: i64, y: i64) -> i64 { return ag_node(tape, nb, 3, x, y, nx_f32_sub(tape[x*5+3], tape[y*5+3])) }
38func ag_relu(tape: *i64, nb: *i64, x: i64) -> i64 {
39 var v: i64 = tape[x*5+3]
40 if nx_f32_lt(v, 0) == 1 { v = 0 }
41 return ag_node(tape, nb, 4, x, 0, v)
42}
43func ag_backward(tape: *i64, nt: i64, root: i64) -> i64 {
44 var i: i64 = 0
45 while i < nt { tape[i*5+4] = 0; i = i + 1 }
46 tape[root*5+4] = nx_i32_to_f32(1)
47 var r: i64 = nt - 1
48 while r >= 0 {
49 let op: i64 = tape[r*5+0]
50 let g: i64 = tape[r*5+4]
51 let ai: i64 = tape[r*5+1]
52 let bi: i64 = tape[r*5+2]
53 if op == 1 {
54 tape[ai*5+4] = nx_f32_add(tape[ai*5+4], g)
55 tape[bi*5+4] = nx_f32_add(tape[bi*5+4], g)
56 }
57 if op == 2 {
58 tape[ai*5+4] = nx_f32_add(tape[ai*5+4], nx_f32_mul(g, tape[bi*5+3]))
59 tape[bi*5+4] = nx_f32_add(tape[bi*5+4], nx_f32_mul(g, tape[ai*5+3]))
60 }
61 if op == 3 {
62 tape[ai*5+4] = nx_f32_add(tape[ai*5+4], g)
63 tape[bi*5+4] = nx_f32_add(tape[bi*5+4], nx_f32_neg(g))
64 }
65 if op == 4 {
66 if nx_f32_gt(tape[ai*5+3], 0) == 1 { tape[ai*5+4] = nx_f32_add(tape[ai*5+4], g) }
67 }
68 r = r - 1
69 }
70 return 0
71}
72func gc_fwd(w1: i64, b1: i64, w2: i64, b2: i64, x: i64) -> i64 {
73 var a: i64 = nx_f32_add(nx_f32_mul(w1, x), b1)
74 if nx_f32_lt(a, 0) == 1 { a = 0 }
75 let y: i64 = nx_f32_add(nx_f32_mul(a, w2), b2)
76 return nx_f32_mul(y, y)
77}
78func gate_a(tape: *i64, nb: *i64) -> i64 {
79 let pv: *i64 = sys_mmap(64) as *i64
80 pv[0] = ag_q(2,3)
81 pv[1] = ag_q(1,4)
82 pv[2] = ag_q(3,2)
83 pv[3] = ag_q(1,4)
84 let xv: i64 = ag_q(3,4)
85 nb[0] = 0
86 let pix: *i64 = sys_mmap(64) as *i64
87 var k: i64 = 0
88 while k < 4 { pix[k] = ag_leaf(tape, nb, pv[k]); k = k + 1 }
89 let xn: i64 = ag_leaf(tape, nb, xv)
90 let a: i64 = ag_relu(tape, nb, ag_add(tape, nb, ag_mul(tape, nb, pix[0], xn), pix[1]))
91 let y: i64 = ag_add(tape, nb, ag_mul(tape, nb, a, pix[2]), pix[3])
92 let loss: i64 = ag_mul(tape, nb, y, y)
93 ag_backward(tape, nb[0], loss)
94 let h: i64 = ag_q(1,128)
95 let wv: *i64 = sys_mmap(64) as *i64
96 var pass: i64 = 1
97 k = 0
98 while k < 4 {
99 var j: i64 = 0
100 while j < 4 { wv[j] = pv[j]; j = j + 1 }
101 wv[k] = nx_f32_add(pv[k], h)
102 let fp: i64 = gc_fwd(wv[0], wv[1], wv[2], wv[3], xv)
103 wv[k] = nx_f32_sub(pv[k], h)
104 let fm: i64 = gc_fwd(wv[0], wv[1], wv[2], wv[3], xv)
105 let fdif: i64 = nx_f32_div(nx_f32_sub(fp, fm), ag_q(1,64))
106 let ga: i64 = tape[pix[k]*5+4]
107 var den: i64 = nx_f32_abs(fdif)
108 if nx_f32_lt(den, ag_q(1,64)) == 1 { den = ag_q(1,64) }
109 let rel: i64 = nx_f32_div(nx_f32_abs(nx_f32_sub(ga, fdif)), den)
110 _ag_puts(" gradcheck p" as *u8); _ag_num(k)
111 _ag_puts(" analytic-milli=" as *u8); _ag_num(ag_milli(ga))
112 _ag_puts(" finite-diff-milli=" as *u8); _ag_num(ag_milli(fdif))
113 _ag_puts(" rel-milli=" as *u8); _ag_num(ag_milli(rel)); _ag_puts("\n" as *u8)
114 if nx_f32_lt(rel, ag_q(1,32)) == 0 { pass = 0 }
115 k = k + 1
116 }
117 return pass
118}
119func ag_train(tape: *i64, nb: *i64, out: *i64) -> i64 {
120 let wt: i64 = ag_q(3,2)
121 let bt: i64 = nx_f32_neg(ag_q(1,2))
122 let xc: *i64 = sys_mmap(128) as *i64
123 let yc: *i64 = sys_mmap(128) as *i64
124 var i: i64 = 0
125 while i < 8 { xc[i] = ag_q(i, 4); yc[i] = nx_f32_add(nx_f32_mul(wt, xc[i]), bt); i = i + 1 }
126 let lr: i64 = ag_q(1,20)
127 let inv8: i64 = ag_q(1,8)
128 var w: i64 = 0
129 var b: i64 = 0
130 var first: i64 = 0
131 var loss_v: i64 = 0
132 var ep: i64 = 0
133 while ep < 500 {
134 nb[0] = 0
135 let wl: i64 = ag_leaf(tape, nb, w)
136 let bl: i64 = ag_leaf(tape, nb, b)
137 var acc: i64 = ag_leaf(tape, nb, 0)
138 var k: i64 = 0
139 while k < 8 {
140 let xn: i64 = ag_leaf(tape, nb, xc[k])
141 let yn: i64 = ag_leaf(tape, nb, yc[k])
142 let e: i64 = ag_sub(tape, nb, ag_add(tape, nb, ag_mul(tape, nb, wl, xn), bl), yn)
143 acc = ag_add(tape, nb, acc, ag_mul(tape, nb, e, e))
144 k = k + 1
145 }
146 let il: i64 = ag_leaf(tape, nb, inv8)
147 let loss: i64 = ag_mul(tape, nb, acc, il)
148 ag_backward(tape, nb[0], loss)
149 loss_v = tape[loss*5+3]
150 if ep == 0 { first = loss_v }
151 w = nx_f32_sub(w, nx_f32_mul(lr, tape[wl*5+4]))
152 b = nx_f32_sub(b, nx_f32_mul(lr, tape[bl*5+4]))
153 if ep % 100 == 0 { _ag_puts(" epoch=" as *u8); _ag_num(ep); _ag_puts(" loss-milli=" as *u8); _ag_num(ag_milli(loss_v)); _ag_puts("\n" as *u8) }
154 ep = ep + 1
155 }
156 out[0] = w
157 out[1] = b
158 out[2] = loss_v
159 out[3] = first
160 return 0
161}
162func main() -> i64 {
163 _ag_puts("=== BUILDER-AUTHORED AUTOGRAD: L1 training substrate rung 1 (sovereign f32, no randomness) ===\n" as *u8)
164 let tape: *i64 = sys_mmap(65536) as *i64
165 let nb: *i64 = sys_mmap(16) as *i64
166 let pa: i64 = gate_a(tape, nb)
167 if pa == 1 { _ag_puts(" GATE A gradcheck: PASS (analytic == finite difference on every param)\n" as *u8) } else { _ag_puts(" GATE A gradcheck: FAIL\n" as *u8) }
168 let o1: *i64 = sys_mmap(64) as *i64
169 let o2: *i64 = sys_mmap(64) as *i64
170 ag_train(tape, nb, o1)
171 _ag_puts(" run1: w-milli=" as *u8); _ag_num(ag_milli(o1[0]))
172 _ag_puts(" b-milli=" as *u8); _ag_num(ag_milli(o1[1]))
173 _ag_puts(" final-loss-milli=" as *u8); _ag_num(ag_milli(o1[2]))
174 _ag_puts(" first-loss-milli=" as *u8); _ag_num(ag_milli(o1[3])); _ag_puts("\n" as *u8)
175 var pb: i64 = 1
176 if nx_f32_lt(o1[2], ag_q(1,1000)) == 0 { pb = 0 }
177 if nx_f32_lt(nx_f32_abs(nx_f32_sub(o1[0], ag_q(3,2))), ag_q(1,16)) == 0 { pb = 0 }
178 if nx_f32_lt(nx_f32_abs(nx_f32_sub(o1[1], nx_f32_neg(ag_q(1,2)))), ag_q(1,16)) == 0 { pb = 0 }
179 if nx_f32_lt(o1[2], o1[3]) == 0 { pb = 0 }
180 if pb == 1 { _ag_puts(" GATE B learns: PASS (loss < 1/1000, params recovered, loss decreased)\n" as *u8) } else { _ag_puts(" GATE B learns: FAIL\n" as *u8) }
181 ag_train(tape, nb, o2)
182 var pc: i64 = 1
183 if o1[0] != o2[0] { pc = 0 }
184 if o1[1] != o2[1] { pc = 0 }
185 if o1[2] != o2[2] { pc = 0 }
186 if pc == 1 { _ag_puts(" GATE C bit-exact training: PASS (two from-scratch runs -> identical weight BITS; the EXCEED axis)\n" as *u8) } else { _ag_puts(" GATE C bit-exact training: FAIL\n" as *u8) }
187 if pa == 1 { if pb == 1 { if pc == 1 {
188 _ag_puts(" AUTOGRAD RUNG-1 GATE: PASS (the team can compute verified gradients and train, bit-exactly, bits-up)\n" as *u8)
189 sys_exit(0)
190 return 0
191 } } }
192 _ag_puts(" AUTOGRAD RUNG-1 GATE: FAIL\n" as *u8)
193 sys_exit(1)
194 return 1
195}