code wiki / _hdl_build / nx_converge_lib.nx

nx_converge_lib.nx source

↩ module page · 154 lines · 7413 B

1// nx_converge_lib.nx -- THE CONVERGENCE CONTROLLER: hit a RETENTION TARGET instead of guessing a denoise. 2// 3// THE PROBLEM. Reference-conditioned generation has one continuous knob (denoise strength) and one 4// thing you actually care about (how much of the reference survived). Today an operator guesses a 5// denoise, waits 25-50s for the GPU, eyeballs the result, and guesses again. The guess is the whole 6// workflow, and it is neither reproducible nor auditable. 7// 8// THE INSIGHT THAT MAKES THIS EXACT, NOT HEURISTIC. nx_refbench_gate's T4 proves the ruler ORDERS 9// correctly, and the measured curve falls monotonically with denoise (859 -> 647 -> 359). A monotonic 10// function on a bounded interval is exactly the case where BISECTION IS GUARANTEED TO CONVERGE, and to 11// do so in ceil(log2(range/tolerance)) steps. So this is not a tuned heuristic with magic gains -- it 12// is a bracketing search with a proof behind it, and its step count is known BEFORE the first render. 13// Retention DECREASES as denoise rises, so the bracket updates are inverted relative to a naive search: 14// measuring ABOVE target means we kept too much and must push denoise UP. 15// 16// WHY IT IS STATELESS. The render happens on the GPU host; only the DECISION belongs in the sovereign 17// organ. So `next` takes the whole bracket as arguments and returns the next probe -- an agent, a 18// workflow, or a human can drive it one render at a time over MCP with nothing persisted between calls. 19// State that lives in a daemon cannot be replayed; state that travels in the arguments can. 20// 21// NO-FLOAT: every quantity is permil (0..1000) integer, per the sovereign arithmetic law. 22// license_tier: ORIGINAL 23import "nx_syscalls.nx" 24 25const CV_MIN: i64 = 0 // denoise floor, permil (0 = return the source untouched) 26const CV_MAX: i64 = 1000 // denoise ceiling, permil (1000 = ignore the source entirely) 27const CV_MAX_STEPS: i64 = 12 // ceil(log2(1000)) = 10, +2 slack; a HARD bound so a non-monotonic 28 // or noisy oracle can never spin the GPU forever 29const CV_DONE: i64 = 0 - 1 // sentinel returned instead of a probe when the search has finished 30 31// Status codes -- distinct so a caller can branch without parsing prose. 32const CV_OK: i64 = 0 // converged: |measured - target| <= tol 33const CV_CONTINUE: i64 = 1 // keep going, a new probe is available 34const CV_EXHAUSTED: i64 = 2 // step budget spent without landing inside tolerance 35const CV_UNREACHABLE: i64 = 3 // target lies outside what the knob can produce 36 37// The first probe. Midpoint of the bracket -- with no measurement yet there is no information that 38// would justify starting anywhere else, and pretending otherwise would be a tuned constant. 39func cv_first_probe() -> i64 { 40 return (CV_MIN + CV_MAX) / 2 41} 42 43// Is this target achievable at all? Retention is bounded by construction: the ruler cannot report 44// more than 1000 (perfect) and a target at/below what maximum denoise yields is not a real request. 45// Answering this BEFORE any render is the difference between a fast refusal and a wasted GPU minute. 46func cv_reachable(target: i64) -> i64 { 47 if target > CV_MAX { return 0 } 48 if target < CV_MIN { return 0 } 49 return 1 50} 51 52// One control step. Given the bracket [lo,hi], the probe just rendered, and what the ruler MEASURED 53// for it, return the next probe (or CV_DONE) and write the status to out_status, the new bracket to 54// out_lo/out_hi. 55// 56// The inversion is the whole subtlety: retention falls as denoise rises, so 57// measured ABOVE target -> too much of the reference survived -> raise the FLOOR (lo = probe) 58// measured BELOW target -> too little survived -> lower the CEILING (hi = probe) 59func cv_step(target: i64, tol: i64, lo: i64, hi: i64, probe: i64, measured: i64, 60 steps_used: i64, out_status: *i64, out_lo: *i64, out_hi: *i64) -> i64 { 61 out_lo[0] = lo 62 out_hi[0] = hi 63 64 if cv_reachable(target) == 0 { out_status[0] = CV_UNREACHABLE; return CV_DONE } 65 66 var err: i64 = measured - target 67 if err < 0 { err = 0 - err } 68 if err <= tol { out_status[0] = CV_OK; return CV_DONE } 69 70 var nlo: i64 = lo 71 var nhi: i64 = hi 72 if measured > target { nlo = probe } else { nhi = probe } 73 out_lo[0] = nlo 74 out_hi[0] = nhi 75 76 if steps_used >= CV_MAX_STEPS { out_status[0] = CV_EXHAUSTED; return CV_DONE } 77 78 // Bracket collapsed to adjacent integers: the knob has no finer setting to offer. Reporting 79 // EXHAUSTED here is honest -- the target sits between two achievable outputs. 80 if nhi - nlo <= 1 { out_status[0] = CV_EXHAUSTED; return CV_DONE } 81 82 out_status[0] = CV_CONTINUE 83 return (nlo + nhi) / 2 84} 85 86// ---- The grounded oracle ------------------------------------------------------------------- 87// A synthetic stand-in for the GPU used by `simulate` and by the gate, so the controller can be 88// proven WITHOUT burning renders. It is NOT invented: it interpolates the RETENTION VALUES ACTUALLY 89// MEASURED on the deployed engine (denoise 350 -> 859, 550 -> 647, 750 -> 359), anchored at the two 90// endpoints the definition forces (denoise 0 returns the source, so retention is perfect; maximum 91// denoise keeps only whatever the prompt happens to reproduce). 92// Using the real curve means a controller that converges here is solving the real problem shape. 93const CV_K0_D: i64 = 0 94const CV_K0_R: i64 = 1000 95const CV_K1_D: i64 = 350 96const CV_K1_R: i64 = 859 97const CV_K2_D: i64 = 550 98const CV_K2_R: i64 = 647 99const CV_K3_D: i64 = 750 100const CV_K3_R: i64 = 359 101const CV_K4_D: i64 = 1000 102const CV_K4_R: i64 = 200 103 104// integer linear interpolation between two keypoints 105func cv_lerp(d: i64, d0: i64, r0: i64, d1: i64, r1: i64) -> i64 { 106 let span: i64 = d1 - d0 107 if span <= 0 { return r0 } 108 return r0 + ((r1 - r0) * (d - d0)) / span 109} 110 111func cv_oracle(denoise: i64) -> i64 { 112 var d: i64 = denoise 113 if d < CV_MIN { d = CV_MIN } 114 if d > CV_MAX { d = CV_MAX } 115 if d <= CV_K1_D { return cv_lerp(d, CV_K0_D, CV_K0_R, CV_K1_D, CV_K1_R) } 116 if d <= CV_K2_D { return cv_lerp(d, CV_K1_D, CV_K1_R, CV_K2_D, CV_K2_R) } 117 if d <= CV_K3_D { return cv_lerp(d, CV_K2_D, CV_K2_R, CV_K3_D, CV_K3_R) } 118 return cv_lerp(d, CV_K3_D, CV_K3_R, CV_K4_D, CV_K4_R) 119} 120 121// Drive the controller to completion against the oracle. Returns the final status; writes the 122// landing denoise, its retention, and the step count. This is the same control path a real GPU run 123// takes -- only the measurement source differs. 124func cv_run(target: i64, tol: i64, out_denoise: *i64, out_ret: *i64, out_steps: *i64) -> i64 { 125 var lo: i64 = CV_MIN 126 var hi: i64 = CV_MAX 127 var probe: i64 = cv_first_probe() 128 var steps: i64 = 0 129 let st: *i64 = sys_mmap(8) 130 let nlo: *i64 = sys_mmap(8) 131 let nhi: *i64 = sys_mmap(8) 132 st[0] = CV_CONTINUE 133 134 if cv_reachable(target) == 0 { 135 out_denoise[0] = 0 - 1 136 out_ret[0] = 0 - 1 137 out_steps[0] = 0 138 return CV_UNREACHABLE 139 } 140 141 var measured: i64 = 0 142 while st[0] == CV_CONTINUE { 143 measured = cv_oracle(probe) 144 steps = steps + 1 145 let nxt: i64 = cv_step(target, tol, lo, hi, probe, measured, steps, st, nlo, nhi) 146 lo = nlo[0] 147 hi = nhi[0] 148 if nxt != CV_DONE { probe = nxt } 149 } 150 out_denoise[0] = probe 151 out_ret[0] = measured 152 out_steps[0] = steps 153 return st[0] 154}