code wiki / _hdl_build / nx_interp_gate.nx

nx_interp_gate.nx source

↩ module page · 119 lines · 6339 B

1// nx_interp_gate.nx -- proves + MEASURES the sovereign snapshot interpolation buffer (nx_interp): 2// under packet LOSS + REORDERING, render-in-the-past interpolation reconstructs the peer's true path 3// (loss becomes invisible) while naive snap-to-latest (zero-order hold) staircases. This is what keeps 4// the bad-mobile-network player smooth. 5// 1) reorder-safe + correct: out-of-order pushes -> time-sorted history; interp ~= truth (loss invisible) 6// 2) EXCEED vs naive (neg-control = zero-order hold): interp max error << naive max error (measured) 7// 3) SMOOTHNESS: interp max per-step jump < naive max per-step jump (measured) 8// 4) yaw shortest-arc: 350deg -> 10deg interpolates through 0 (not the long way) 9// 5) no-data (neg): empty history -> sample returns 0 (no fabricated pose) 10// 6) clamp: before-first -> status 2, at/after-last -> status 3 (hold; extrapolate = NET-R2) 11// Built nx_cc_sovereign -> nxasm_x86 (no gcc, no .sh). license_tier: ORIGINAL 12import "nx_syscalls.nx" 13import "nx_gate_emit_lib.nx" 14import "nx_interp.nx" 15import "nx_gate_verdict.nx" 16 17func g_abs(v: i64) -> i64 { if v < 0 { return 0 - v } return v } 18// build a snapshot with x=val (other fields fixed) for the path proof 19func mkf(f: *i64, x: i64) -> i64 { var i: i64=0; while i<10 { f[i]=0; i=i+1 } f[0]=x; return 0 } 20// naive zero-order hold: value of the newest RECEIVED sample with time <= rt; val(t)=t/2 21func naive_x(times: *i64, nr: i64, rt: i64) -> i64 { 22 var best: i64 = times[0] 23 var i: i64 = 0 24 while i < nr { if times[i] <= rt { best = times[i] } i = i + 1 } 25 return best / 2 26} 27 28func main() -> i64 { 29 g_puts("nx_interp gate (sovereign snapshot interpolation: smooth under loss+jitter+reorder)\n" as *u8) 30 var pass: i64 = 0; var total: i64 = 0 31 32 let K: i64 = 16 33 let hist: *i64 = sys_mmap((1 + K*IP_STRIDE) * 8) as *i64 34 ip_init(hist) 35 let f: *i64 = sys_mmap(10*8) as *i64 36 let out: *i64 = sys_mmap(10*8) as *i64 37 38 // ground truth path val(t)=t/2 sampled at 0,100,...,1000; DROP 300/600/900 (loss); push 500 BEFORE 400 (reorder) 39 let times: *i64 = sys_mmap(8*8) as *i64 40 times[0]=0; times[1]=100; times[2]=200; times[3]=400; times[4]=500; times[5]=700; times[6]=800; times[7]=1000 41 // push in a deliberately out-of-order sequence 42 mkf(f, 0); ip_push(hist, K, 0, f) 43 mkf(f, 50); ip_push(hist, K, 100, f) 44 mkf(f, 100); ip_push(hist, K, 200, f) 45 mkf(f, 250); ip_push(hist, K, 500, f) // 500 pushed BEFORE 400 (reorder) 46 mkf(f, 200); ip_push(hist, K, 400, f) 47 mkf(f, 350); ip_push(hist, K, 700, f) 48 mkf(f, 400); ip_push(hist, K, 800, f) 49 mkf(f, 500); ip_push(hist, K, 1000, f) 50 51 // 1) reorder-safe (sorted) + correct (interp ~= truth despite the 3 drops) 52 var sorted: i64 = 1 53 var i: i64 = 1 54 while i < ip_count(hist) { if ip_time(hist, i-1) > ip_time(hist, i) { sorted = 0 } i = i + 1 } 55 var ei_max: i64 = 0; var en_max: i64 = 0 56 var si_max: i64 = 0; var sn_max: i64 = 0 57 var prev_i: i64 = 0 - 1; var prev_n: i64 = 0 - 1 58 var rt: i64 = 150 59 while rt <= 850 { 60 ip_sample(hist, rt, out) 61 let ix: i64 = out[0] 62 let truev: i64 = rt / 2 63 let ei: i64 = g_abs(ix - truev); if ei > ei_max { ei_max = ei } 64 let nxv: i64 = naive_x(times, 8, rt) 65 let en: i64 = g_abs(nxv - truev); if en > en_max { en_max = en } 66 if prev_i >= 0 { let s: i64 = g_abs(ix - prev_i); if s > si_max { si_max = s } } 67 if prev_n >= 0 { let s: i64 = g_abs(nxv - prev_n); if s > sn_max { sn_max = s } } 68 prev_i = ix; prev_n = nxv 69 rt = rt + 50 70 } 71 g_puts(" [measure] over a lossy(3 drops)+reordered stream: interp_max_err=" as *u8); g_pn(ei_max) 72 g_puts(" naive_max_err=" as *u8); g_pn(en_max); g_puts("\n" as *u8) 73 g_puts(" [measure] per-step jump: interp_max=" as *u8); g_pn(si_max); g_puts(" naive_max=" as *u8); g_pn(sn_max); g_puts("\n" as *u8) 74 var r1: i64 = 1 75 if sorted != 1 { r1 = 0 } 76 if ei_max > 2 { r1 = 0 } // interp reconstructs truth within 2 voxels (loss invisible) 77 pass = pass + g_check("reorder-safe + interp reconstructs truth under loss" as *u8, r1); total=total+1 78 79 // 2) EXCEED vs naive ZOH (measured) 80 pass = pass + g_check("interp_max_err < naive_max_err (smoother than snap-to-latest)" as *u8, ei_max < en_max); total=total+1 81 82 // 3) SMOOTHNESS (measured): interp never jumps as hard as naive 83 pass = pass + g_check("interp per-step jump < naive per-step jump" as *u8, si_max < sn_max); total=total+1 84 85 // 4) yaw shortest-arc 86 let hy: *i64 = sys_mmap((1 + 4*IP_STRIDE) * 8) as *i64 87 ip_init(hy) 88 mkf(f, 0); f[3] = 350; ip_push(hy, 4, 0, f) 89 mkf(f, 0); f[3] = 10; ip_push(hy, 4, 100, f) 90 ip_sample(hy, 50, out) 91 let yv: i64 = out[3] 92 var r4: i64 = 0 93 if yv <= 5 { r4 = 1 } 94 if yv >= 355 { r4 = 1 } // ~0deg (== 360) the short way, NOT ~180 95 g_puts(" [measure] yaw lerp 350->10 at half = " as *u8); g_pn(yv); g_puts(" deg (expect ~0/360, not ~180)\n" as *u8) 96 pass = pass + g_check("yaw interpolates shortest-arc through 0" as *u8, r4); total=total+1 97 98 // 5) no-data neg-control 99 let he: *i64 = sys_mmap((1 + 4*IP_STRIDE) * 8) as *i64 100 ip_init(he) 101 let st0: i64 = ip_sample(he, 500, out) 102 pass = pass + g_check("empty history -> no fabricated pose (status 0)" as *u8, st0 == 0); total=total+1 103 104 // 6) clamp before-first / at-after-last 105 let sbefore: i64 = ip_sample(hist, 0 - 50, out) 106 let safter: i64 = ip_sample(hist, 5000, out) 107 pass = pass + g_check("clamp: before-first=2, after-last=3" as *u8, (sbefore == 2) & (safter == 3)); total=total+1 108 109 g_puts("---- interp gate: passed " as *u8); g_pn(pass); g_puts(" / " as *u8); g_pn(total); g_puts(" ----\n" as *u8) 110 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 111 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 112 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 113 let ctr__dry: *i64 = gv_ctr() 114 ctr__dry[0] = pass 115 ctr__dry[1] = total 116 let rc__dry: i64 = gv_verdict("INTERP-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 117 sys_exit(rc__dry) 118 return rc__dry 119}