nx_autograd.nx
buildroot/runtime/nx_autograd.nx
about
nx_autograd.nx -- TRAIN-R1: scalar tape-based reverse-mode autograd. THE training keystone: once gradients
are correct and an optimizer drives a loss down, every model the team trains (the FNet text model, the
pixel engine) is the SAME loop at larger scale. Inference is already sovereign; this is the missing BACKWARD
half. Spec: knowledge/specs/2026-06-09-tutoring-training-substrate-rung1.md.
TAPE: stride-5 nodes {op, ai, bi, val, grad} in one i64 array. Construction is EAGER -- creating a node
computes its f32 `val` via the EXISTING nx_f32 ops (reuse, never reimplement). Backward = ONE reverse sweep
(nodes are built in topological order, so reverse order visits parents before children) accumulating into
`grad`: add: g->both | mul: ga+=g*vb, gb+=g*va | sub: ga+=g, gb+=-g | relu: pass g iff input val>0.
Seed grad[root]=1.0. The whole engine is ~50 lines BECAUSE the f32 numeric tower already exists.
genealogy_id: linnainmaa_1970_reverse_mode_ad + rumelhart_1986_backprop
lineage_id: sovereign_scalar_tape_autograd_v1
license_tier: ORIGINAL verdict: GREEN (nx_train_r1_gate 2026-06-14: gradcheck + model-learns + bit-exact)
dependencies 4 imports · 9 importers
imports: nx_f32.nxnx_f32_div.nxnx_f32_cvt.nxnx_syscalls.nx
imported by: nx_mt_core.nxnx_mt_r0_gate.nxnx_mt_r1_gate.nxnx_mt_r2_gate.nxnx_mt_r3_gate.nxnx_nofloat_vs_float_gate.nxnx_reader_mlp_gate.nxnx_reader_mlp_train.nxnx_train_r1_gate.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 20 | const AG_LEAF: i64 = 0 |
| 21 | const AG_ADD: i64 = 1 |
| 22 | const AG_MUL: i64 = 2 |
| 23 | const AG_SUB: i64 = 3 |
| 24 | const AG_RELU: i64 = 4 |
| 25 | const AG_F32_ZERO: i64 = 0 // IEEE-754 binary32 +0.0 |
| 26 | const AG_F32_ONE: i64 = 1065353216 // IEEE-754 binary32 1.0 = 0x3F800000 |
functions
| 28 | func ag_val(tape: *i64, k: i64) -> i64 { return tape[5 * k + 3] } |
| 29 | func ag_grad(tape: *i64, k: i64) -> i64 { return tape[5 * k + 4] } |
| 32 | func ag_leaf(tape: *i64, np: *i64, v: i64) -> i64 |
| 38 | func ag_mk(tape: *i64, np: *i64, op: i64, a: i64, b: i64, v: i64) -> i64 |
| 44 | func ag_add(tape: *i64, np: *i64, a: i64, b: i64) -> i64 |
| 47 | func ag_mul(tape: *i64, np: *i64, a: i64, b: i64) -> i64 |
| 50 | func ag_sub(tape: *i64, np: *i64, a: i64, b: i64) -> i64 |
| 53 | func ag_relu(tape: *i64, np: *i64, a: i64) -> i64 |
| 60 | func ag_backward(tape: *i64, n: i64, root: i64) -> i64 called by 9: mainmt_trainmt_trainm2_trainfloat_gradsmain+3 calls 4: nx_f32_addnx_f32_mulnx_f32_negnx_f32_gt |
| 93 | func ag_constf(num: i64, den: i64) -> i64 { return nx_f32_div(nx_i32_to_f32(num), nx_i32_to_f32(den)) } called by 18: mt_f32_to_millimt_trainmainmr_f32_to_millimainm2_f32_to_milli+12 calls 2: nx_f32_divnx_i32_to_f32 |
| 96 | func main() -> i64 |