code wiki / _hdl_build / nx_ng_diffrender.nx
nx_ng_diffrender.nx source
↩ module page · 114 lines · 6214 B
1// nx_ng_diffrender.nx -- CAP-DIFF-RENDER, the next missing generation past the sovereign-raster substrate
2// (nx_ng_gap: prereqs CAP-RASTER + CAP-NF-AUTOGRAD BUILT -> this is the adjacent-possible build). The FIRST
3// rung of differentiable rendering: gradients of rendered PIXELS w.r.t. a SCENE PARAMETER, the substrate
4// NeRF / 3D-Gaussian-Splatting optimization is built on.
5//
6// Minimal honest proof (operator: measured, no wave; HARD evidence per the genealogy ladder): a 1D soft-edge
7// rasterizer renders an image from one scene param p (the edge position) in Q16 fixed-point. We then OPTIMIZE
8// p by gradient descent to match a target image (rendered at p*). Correctness is proven the rigorous way:
9// T1 DESCENT converges -- GD on p drives loss -> ~0 and p -> p* (the gradient points the right way),
10// T2 NEG-CONTROL diverges -- ASCENDING the same gradient INCREASES loss (the gradient has teeth/direction),
11// T3 finite-difference SIGN check -- sign(analytic dLoss/dp) == sign((L(p+e)-L(p-e))) (the gradient is real),
12// T4 zero-at-optimum -- loss(p*) == 0 (the render + loss are consistent).
13// Q16 fixed-point only (no float), sovereign syscalls only. license_tier: ORIGINAL expect_exit: 0
14import "nx_syscalls.nx"
15
16const Q: i64 = 65536 // 1.0 in Q16
17const HALF: i64 = 32768 // 0.5
18const NPX: i64 = 16 // canvas pixels
19const INVS: i64 = 16384 // 1/s with softness s=4 px (Q16/4)
20const PSTAR: i64 = 524288 // target edge position = 8.0 px
21const P0: i64 = 196608 // initial guess = 3.0 px
22const LR: i64 = 4096 // learning rate = 0.0625 (Q16)
23const NSTEP: i64 = 120
24const EPS: i64 = 16384 // finite-diff step = 0.25 px
25const PCHK: i64 = 393216 // sign-check point = 6.0 px
26const TOL: i64 = 32768 // convergence tolerance = 0.5 px
27
28func dp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
29func dpn(v: i64) -> i64 {
30 let bb: *u8 = sys_mmap(28); var m: i64 = v
31 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
32 let t: *u8 = sys_mmap(28); var k: i64 = 0
33 if m == 0 { t[0] = 48 as u8; k = 1 }
34 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
35 var i: i64 = 0; while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
36 sys_write(1, bb, k); return 0
37}
38func dr_qm(a: i64, b: i64) -> i64 { return (a * b) >> 16 }
39func dr_clamp(u: i64) -> i64 { if u < 0 { return 0 } if u > Q { return Q } return u }
40// soft-edge coverage of pixel i for edge position p (Q16): cov = clamp((i-p)/s + 0.5, 0, 1)
41func dr_cov(i: i64, p: i64) -> i64 { let di: i64 = i * Q - p; let u: i64 = dr_qm(di, INVS) + HALF; return dr_clamp(u) }
42// is pixel i in the differentiable transition band (0<u<1) for p? (where dcov/dp != 0)
43func dr_active(i: i64, p: i64) -> i64 { let di: i64 = i * Q - p; let u: i64 = dr_qm(di, INVS) + HALF; if u > 0 { if u < Q { return 1 } } return 0 }
44func dr_render(p: i64, out: *i64) -> i64 { var i: i64 = 0; while i < NPX { out[i] = dr_cov(i, p); i = i + 1 } return 0 }
45func dr_loss(p: i64, tgt: *i64) -> i64 { var s: i64 = 0; var i: i64 = 0; while i < NPX { let d: i64 = dr_cov(i, p) - tgt[i]; s = s + dr_qm(d, d); i = i + 1 } return s }
46// analytic dLoss/dp = sum_i 2*(cov_i - tgt_i)*dcov_i/dp ; dcov/dp = -1/s = -INVS (Q16) on the active band, else 0
47func dr_grad(p: i64, tgt: *i64) -> i64 {
48 var g: i64 = 0; var i: i64 = 0
49 while i < NPX {
50 let cov: i64 = dr_cov(i, p)
51 var dcov: i64 = 0
52 if dr_active(i, p) == 1 { dcov = 0 - INVS }
53 let d: i64 = cov - tgt[i]
54 g = g + 2 * dr_qm(d, dcov)
55 i = i + 1
56 }
57 return g
58}
59
60func main() -> i64 {
61 dp("=== nx_ng_diffrender: CAP-DIFF-RENDER -- gradients through a 1D soft-raster (Q16, no float) ===\n" as *u8)
62 let tgt: *i64 = sys_mmap(8 * NPX)
63 dr_render(PSTAR, tgt)
64 let loss0: i64 = dr_loss(P0, tgt)
65 dp(" target p*=" as *u8); dpn(PSTAR >> 16); dp(".0px init p0=" as *u8); dpn(P0 >> 16); dp(".0px loss0=" as *u8); dpn(loss0); dp("\n" as *u8)
66
67 // T1: gradient DESCENT on the scene param
68 var p: i64 = P0
69 var step: i64 = 0
70 while step < NSTEP {
71 let g: i64 = dr_grad(p, tgt)
72 p = p - dr_qm(LR, g)
73 step = step + 1
74 if step % 30 == 0 { dp(" step " as *u8); dpn(step); dp(" loss=" as *u8); dpn(dr_loss(p, tgt)); dp(" p=" as *u8); dpn(p >> 16); dp("(.q" as *u8); dpn(p); dp(")\n" as *u8) }
75 }
76 let lossN: i64 = dr_loss(p, tgt)
77 var dpos: i64 = p - PSTAR
78 if dpos < 0 { dpos = 0 - dpos }
79 dp(" T1 DESCENT: final loss=" as *u8); dpn(lossN); dp(" p=" as *u8); dpn(p); dp(" |p-p*|=" as *u8); dpn(dpos); dp("\n" as *u8)
80
81 // T2: NEG-CONTROL -- ASCEND the gradient (wrong sign) -> must diverge
82 var pa: i64 = P0
83 var s2: i64 = 0
84 while s2 < NSTEP { let g2: i64 = dr_grad(pa, tgt); pa = pa + dr_qm(LR, g2); s2 = s2 + 1 }
85 let lossA: i64 = dr_loss(pa, tgt)
86 dp(" T2 ASCENT(neg-control): final loss=" as *u8); dpn(lossA); dp(" (must be > loss0)\n" as *u8)
87
88 // T3: finite-difference SIGN check at p=PCHK
89 let ga: i64 = dr_grad(PCHK, tgt)
90 let lp: i64 = dr_loss(PCHK + EPS, tgt)
91 let lm: i64 = dr_loss(PCHK - EPS, tgt)
92 let fd: i64 = lp - lm
93 var sign_ok: i64 = 0
94 if ga > 0 { if fd > 0 { sign_ok = 1 } }
95 if ga < 0 { if fd < 0 { sign_ok = 1 } }
96 if ga == 0 { if fd == 0 { sign_ok = 1 } }
97 dp(" T3 GRADCHECK(sign): analytic=" as *u8); dpn(ga); dp(" finite-diff=" as *u8); dpn(fd); dp(" match=" as *u8); dpn(sign_ok); dp("\n" as *u8)
98
99 // T4: zero at optimum
100 let loss_star: i64 = dr_loss(PSTAR, tgt)
101 dp(" T4 loss(p*)=" as *u8); dpn(loss_star); dp(" (must be 0)\n" as *u8)
102
103 var converged: i64 = 0
104 if lossN * 50 < loss0 { if dpos < TOL { converged = 1 } }
105 var ok: i64 = 1
106 if converged != 1 { ok = 0 }
107 if lossA <= loss0 { ok = 0 }
108 if sign_ok != 1 { ok = 0 }
109 if loss_star != 0 { ok = 0 }
110 dp(" converged=" as *u8); dpn(converged); dp("\n verdict=" as *u8)
111 if ok == 1 { dp("GREEN (descent converges, ascent diverges, grad sign matches finite-diff, zero at optimum = real differentiable render)\n" as *u8); sys_exit(0); return 0 }
112 dp("RED\n" as *u8)
113 sys_exit(1); return 1
114}