code wiki / _hdl_build / nx_mulchain_deep.nx

nx_mulchain_deep.nx source

↩ module page · 163 lines · 7816 B

1// nx_mulchain_deep.nx -- the DEEPENED chain search (the team's answer to its own escalation: 2// "constants exceed imul cost -- author imul or deepen the search?"). 3// 4// THEORY (researched, not hand-waved): finding the shortest op-chain for c*x is computing a 5// length-/time-BOUNDED KOLMOGOROV COMPLEXITY of that function over our instruction set. The 6// unbounded version is uncomputable (Berry/halting); we only escape that because our ops are 7// loop-free + total + length-capped. The principled search order is LEVIN's universal search: 8// shortest-first, bounding by length AND cost (Kt = |program| + log time). Our iterative 9// deepening over op-count IS Levin shortest-first; the `+log time` term is the cost cap below. 10// Two prunes keep it tractable: 11// (1) REACHABILITY (the THURBER bound, generalized): an addition-chain element at most 12// DOUBLES per step (prune when 2^m * a_i < n); our op set grows faster (SHL by maxsh, 13// LEA +3 bits), so we use maxmag << (G*rem) < |C| with G = max(maxsh,4). COMPLETE 14// (never prunes a reachable target), collapses the dead subtrees that hung the prime case. 15// (2) COST CAP (Levin's +log time): a serial chain of N 1-cycle ops ~ N cycles latency vs 16// imul ~3 cycles, so searching past the cost-competitive depth is pointless -> cap maxL. 17// NB: the classic Hamming-weight lower bounds (Schonhage log2 n + log2 v(n)) DON'T transfer 18// here -- SHL/LEA do many doublings in one op, so our minimal chains are shorter and those 19// bounds under-count us. The reach bound + cost cap are what actually apply to our op set. 20// Refs: Kolmogorov complexity (uncomputable); Levin universal search / Kt; Thurber 1999 21// (addition-chain branch-and-bound). license_tier: ORIGINAL 22 23import "nx_mulchain.nx" // SO_* ops, mc_abs, mc_avslot, mc_eval, mulchain_find (shallow) 24import "nx_search_governor.nx" // the shared cost-bounded-search judgment (sg_decide, sg_cost_cap) 25 26// max |value| over the operands available at step t: slot 0 (=x) and computed slots 3..t+2. 27func mcd_maxmag(sv: *i64, t: i64) -> i64 { 28 var mx: i64 = mc_abs(sv[0]); var s: i64 = 3 29 while s <= t + 2 { let v: i64 = mc_abs(sv[s]); if v > mx { mx = v } s = s + 1 } 30 return mx 31} 32 33// REACHABILITY: can |C| still be produced in `rem` ops, starting from max magnitude M, given 34// per-op bit-growth G? Upper bound on reachable magnitude = M << (G*rem). Guarded vs overflow. 35func mcd_reach(M: i64, rem: i64, G: i64, C: i64) -> i64 { 36 if rem <= 0 { return 1 } 37 let sh: i64 = G * rem 38 if sh >= 62 { return 1 } // bound exceeds i64 range -> assume reachable 39 if (M << sh) >= mc_abs(C) { return 1 } 40 return 0 41} 42 43// pruned iterative-deepening enumeration (mirrors mc_enum but with the reach prune up front). 44func mcd_enum(op: *i64, a: *i64, b: *i64, t: i64, L: i64, sv: *i64, C: i64, bound: i64, maxsh: i64, G: i64) -> i64 { 45 if t == L { if sv[L + 2] == C { return 1 } return 0 } 46 // FAIL-FAST: if even maximal growth can't reach |C| in the remaining (L-t) ops, give up here. 47 if mcd_reach(mcd_maxmag(sv, t), L - t, G, C) == 0 { return 0 } 48 let nav: i64 = t + 1 49 let rslot: i64 = t + 3 50 // ADD (i <= j) 51 var i: i64 = 0 52 while i < nav { 53 var j: i64 = i 54 while j < nav { 55 let nv: i64 = sv[mc_avslot(i)] + sv[mc_avslot(j)] 56 if mc_abs(nv) <= bound { 57 sv[rslot] = nv; op[t] = SO_ADD; a[t] = mc_avslot(i); b[t] = mc_avslot(j) 58 if mcd_enum(op, a, b, t + 1, L, sv, C, bound, maxsh, G) == 1 { return 1 } 59 } 60 j = j + 1 61 } 62 i = i + 1 63 } 64 // SUB (ordered) 65 i = 0 66 while i < nav { 67 var j: i64 = 0 68 while j < nav { 69 if i != j { 70 let nv: i64 = sv[mc_avslot(i)] - sv[mc_avslot(j)] 71 if mc_abs(nv) <= bound { 72 sv[rslot] = nv; op[t] = SO_SUB; a[t] = mc_avslot(i); b[t] = mc_avslot(j) 73 if mcd_enum(op, a, b, t + 1, L, sv, C, bound, maxsh, G) == 1 { return 1 } 74 } 75 } 76 j = j + 1 77 } 78 i = i + 1 79 } 80 // SHL by 1..maxsh 81 i = 0 82 while i < nav { 83 var sh: i64 = 1 84 while sh <= maxsh { 85 let nv: i64 = sv[mc_avslot(i)] << sh 86 if mc_abs(nv) <= bound { 87 sv[rslot] = nv; op[t] = SO_SHL; a[t] = mc_avslot(i); b[t] = sh 88 if mcd_enum(op, a, b, t + 1, L, sv, C, bound, maxsh, G) == 1 { return 1 } 89 } 90 sh = sh + 1 91 } 92 i = i + 1 93 } 94 // LEA: base + index*{2,4,8} 95 i = 0 96 while i < nav { 97 var j2: i64 = 0 98 while j2 < nav { 99 var si: i64 = 0 100 while si < 3 { 101 var sc: i64 = 2 102 if si == 1 { sc = 4 } 103 if si == 2 { sc = 8 } 104 let nv: i64 = sv[mc_avslot(i)] + sv[mc_avslot(j2)] * sc 105 if mc_abs(nv) <= bound { 106 var oo: i64 = SO_LEA2 107 if sc == 4 { oo = SO_LEA4 } 108 if sc == 8 { oo = SO_LEA8 } 109 sv[rslot] = nv; op[t] = oo; a[t] = mc_avslot(i); b[t] = mc_avslot(j2) 110 if mcd_enum(op, a, b, t + 1, L, sv, C, bound, maxsh, G) == 1 { return 1 } 111 } 112 si = si + 1 113 } 114 j2 = j2 + 1 115 } 116 i = i + 1 117 } 118 return 0 119} 120 121// deepened find: shortest chain for x*C over lengths 1..maxL, pruned so hopeless constants 122// fail fast. maxsh caps single-shift size (G = max(maxsh,4) keeps the reach bound sound vs LEA). 123func mulchain_find_deep(C: i64, maxL: i64, op: *i64, a: *i64, b: *i64) -> i64 { 124 let sv: *i64 = sys_mmap(8 * (maxL + 8)) as *i64 125 sv[0] = 1 126 let bound: i64 = mc_abs(C) * 4 + 8 127 let maxsh: i64 = 6 128 var G: i64 = maxsh 129 if G < 4 { G = 4 } 130 var L: i64 = 1 131 while L <= maxL { 132 if mcd_enum(op, a, b, 0, L, sv, C, bound, maxsh, G) == 1 { return L } 133 L = L + 1 134 } 135 return 0 136} 137 138// VERIFIED deepened find: returns minimal op count only if the chain provably computes c*x on 139// independent inputs (the Engineer's verify-don't-trust), else -1 (cede to imul / not found). 140func mulchain_deep_verified(c: i64, maxL: i64, op: *i64, a: *i64, b: *i64) -> i64 { 141 let L: i64 = mulchain_find_deep(c, maxL, op, a, b) 142 if L <= 0 { return 0 - 1 } 143 if mc_eval(op, a, b, L, 7) != c * 7 { return 0 - 1 } 144 if mc_eval(op, a, b, L, 131) != c * 131 { return 0 - 1 } 145 if mc_eval(op, a, b, L, 0 - 9) != c * (0 - 9) { return 0 - 1 } 146 return L 147} 148 149// COST SELECTION (the escalation's real answer): the deepened search alone doesn't beat imul -- 150// a chain of N serial 1-cycle ops costs ~N cycles, imul ~3. So the team SEARCHES to `cap` and 151// picks the cost-winner. tie_thresh = the largest chain length that still beats/ties imul under 152// the chosen cost model (3 for LATENCY: chain wins at N<=2, ties at 3; larger for THROUGHPUT, 153// where cheap ops spread across ALU ports beat the single multiply unit). Data-driven (no magic 154// numbers -- the cost params are arguments). Returns the chain op-count if the chain is the 155// cost-winner, else 0 = USE IMUL. This RESOLVES "exceed imul cost?" per constant, by cost. 156const MCD_USE_IMUL: i64 = 0 // == SG_USE_BASELINE: the multiply space's baseline is imul 157func mc_cost_select(c: i64, cap: i64, tie_thresh: i64, op: *i64, a: *i64, b: *i64) -> i64 { 158 let L: i64 = mulchain_deep_verified(c, cap, op, a, b) 159 // multiply-by-constant is just ONE instance of the general principle: op_cost = 1 cycle per 160 // chain op, baseline_cost = imul's cost (tie_thresh cycles). The SAME sg_decide governs every 161 // search space -- the team applies one judgment, not a per-space rule. 162 return sg_decide(L, 1, tie_thresh) 163}