code wiki / _hdl_build / nx_synth_oe.nx

nx_synth_oe.nx source

↩ module page · 178 lines · 6913 B

1// nx_synth_oe.nx -- the synthesizer SCALED to S-class technique: bottom-up 2// enumerative synthesis with OBSERVATIONAL-EQUIVALENCE pruning. Two sub-programs 3// that produce the SAME output vector on the example inputs are interchangeable, so 4// only ONE representative per distinct behaviour is kept. The search is then bounded 5// by the number of distinct BEHAVIOURS, not the (exponential) number of programs -- 6// the exact technique SOTA program synthesizers use to scale, and it lets us reach 7// depth-3+ programs the naive <=2 enumerator never could. 8// 9// Research: Udupa et al., "TRANSIT: Specifying Protocols with Concolic Snippets," 10// PLDI 2013 (observational equivalence); Alur et al., EUSolver / SyGuS; Massalin, 11// AFIPS 1987 (superoptimizer). Exceed is MEASURED: candidates tried (P) vs distinct 12// values kept (V); P/V is the collapse the pruning buys, A/B on the same task. 13 14import "nx_syscalls.nx" 15const SOE_MAGIC_3750763034362895579: i64 = 3750763034362895579 16const SOE_MAGIC_1099511628211: i64 = 1099511628211 17 18const SOE_ADD: i64 = 0 19const SOE_SUB: i64 = 1 20const SOE_MUL: i64 = 2 21const SOE_SHL: i64 = 3 22 23const SOE_MAXV: i64 = 4000 24const SOE_HT: i64 = 16384 // power of two 25 26struct SoeCtx { 27 vecs: *i64, // MAXV * nex (output vector per value) 28 kind: *i64, // 0=leaf-x, 1=leaf-const, 2=op 29 op: *i64, 30 pa: *i64, 31 pb: *i64, 32 lit: *i64, 33 ht: *i64, // hash table -> value index, or -1 34 nval: i64, 35 nex: i64, 36 tried: i64, // P: candidate combinations evaluated 37 found: i64, // value index of the target, or -1 38 target: *i64, // target output vector 39} 40 41// recursively evaluate the program rooted at value v on input x (for held-out check). 42func soe_eval(c: *SoeCtx, v: i64, x: i64) -> i64 { 43 let k: i64 = c.kind[v] 44 if k == 0 { return x } 45 if k == 1 { return c.lit[v] } 46 let o: i64 = c.op[v] 47 if o == SOE_SHL { return soe_eval(c, c.pa[v], x) << c.lit[v] } 48 let a: i64 = soe_eval(c, c.pa[v], x) 49 let b: i64 = soe_eval(c, c.pb[v], x) 50 if o == SOE_ADD { return a + b } 51 if o == SOE_SUB { return a - b } 52 if o == SOE_MUL { return a * b } 53 return 0 54} 55 56func soe_hash(tmp: *i64, nex: i64) -> i64 { 57 var h: i64 = 0 - SOE_MAGIC_3750763034362895579 58 var i: i64 = 0 59 while i < nex { h = h ^ tmp[i]; h = h * SOE_MAGIC_1099511628211; i = i + 1 } 60 if h < 0 { h = 0 - h } 61 return h 62} 63func soe_vec_eq(c: *SoeCtx, v: i64, tmp: *i64) -> i64 { 64 var i: i64 = 0 65 let base: i64 = (c.vecs as i64) + v * c.nex * 8 66 while i < c.nex { let p: *i64 = (base + i * 8) as *i64; if p[0] != tmp[i] { return 0 } i = i + 1 } 67 return 1 68} 69// returns existing value idx with vec==tmp, or -1. 70func soe_lookup(c: *SoeCtx, tmp: *i64) -> i64 { 71 var slot: i64 = soe_hash(tmp, c.nex) & (SOE_HT - 1) 72 var guard: i64 = 0 73 while guard < SOE_HT { 74 let v: i64 = c.ht[slot] 75 if v < 0 { return 0 - 1 } 76 if soe_vec_eq(c, v, tmp) == 1 { return v } 77 slot = (slot + 1) & (SOE_HT - 1) 78 guard = guard + 1 79 } 80 return 0 - 1 81} 82// insert tmp as a NEW value with the given program info; returns its index (or -1 if full). 83func soe_insert(c: *SoeCtx, tmp: *i64, kind: i64, op: i64, pa: i64, pb: i64, lit: i64) -> i64 { 84 if c.nval >= SOE_MAXV { return 0 - 1 } 85 let v: i64 = c.nval 86 let base: i64 = (c.vecs as i64) + v * c.nex * 8 87 var i: i64 = 0 88 while i < c.nex { let p: *i64 = (base + i * 8) as *i64; p[0] = tmp[i]; i = i + 1 } 89 c.kind[v] = kind; c.op[v] = op; c.pa[v] = pa; c.pb[v] = pb; c.lit[v] = lit 90 c.nval = v + 1 91 var slot: i64 = soe_hash(tmp, c.nex) & (SOE_HT - 1) 92 while c.ht[slot] >= 0 { slot = (slot + 1) & (SOE_HT - 1) } 93 c.ht[slot] = v 94 // target check 95 if soe_vec_eq(c, v, c.target) == 1 { if c.found < 0 { c.found = v } } 96 return v 97} 98 99// add a candidate vector (already computed in tmp): dedupe, insert if new. 100func soe_offer(c: *SoeCtx, tmp: *i64, op: i64, pa: i64, pb: i64, lit: i64) -> i64 { 101 c.tried = c.tried + 1 102 if soe_lookup(c, tmp) >= 0 { return 0 } 103 soe_insert(c, tmp, 2, op, pa, pb, lit) 104 return 0 105} 106 107// bottom-up synthesis: build distinct behaviours until the target appears or rounds 108// are exhausted. ex_x/ex_y are the spec; returns the root value idx or -1. 109func soe_synth(c: *SoeCtx, ex_x: *i64, ex_y: *i64, nex: i64, rounds: i64) -> i64 { 110 c.nex = nex; c.nval = 0; c.tried = 0; c.found = 0 - 1 111 var i: i64 = 0 112 while i < SOE_HT { c.ht[i] = 0 - 1; i = i + 1 } 113 let tmp: *i64 = sys_mmap(8 * nex) 114 // target vector 115 i = 0; while i < nex { c.target[i] = ex_y[i]; i = i + 1 } 116 // leaves: x, const 1 117 i = 0; while i < nex { tmp[i] = ex_x[i]; i = i + 1 } 118 soe_insert(c, tmp, 0, 0, 0, 0, 0) // leaf x 119 i = 0; while i < nex { tmp[i] = 1; i = i + 1 } 120 soe_insert(c, tmp, 1, 0, 0, 0, 1) // leaf const 1 121 if c.found >= 0 { return c.found } 122 123 var r: i64 = 0 124 while r < rounds { 125 let start: i64 = c.nval 126 var u: i64 = 0 127 while u < start { 128 // SHL u by imm 1..5 129 var sh: i64 = 1 130 while sh < 6 { 131 var j: i64 = 0 132 while j < nex { let pu: *i64 = ((c.vecs as i64) + u * nex * 8 + j * 8) as *i64; tmp[j] = pu[0] << sh; j = j + 1 } 133 soe_offer(c, tmp, SOE_SHL, u, 0, sh) 134 sh = sh + 1 135 } 136 var v: i64 = 0 137 while v < start { 138 var oo: i64 = 0 139 while oo < 3 { 140 var j: i64 = 0 141 while j < nex { 142 let pu: *i64 = ((c.vecs as i64) + u * nex * 8 + j * 8) as *i64 143 let pv: *i64 = ((c.vecs as i64) + v * nex * 8 + j * 8) as *i64 144 var rr: i64 = 0 145 if oo == SOE_ADD { rr = pu[0] + pv[0] } 146 if oo == SOE_SUB { rr = pu[0] - pv[0] } 147 if oo == SOE_MUL { rr = pu[0] * pv[0] } 148 tmp[j] = rr 149 j = j + 1 150 } 151 soe_offer(c, tmp, oo, u, v, 0) 152 oo = oo + 1 153 } 154 v = v + 1 155 } 156 if c.found >= 0 { return c.found } 157 u = u + 1 158 } 159 if c.found >= 0 { return c.found } 160 r = r + 1 161 } 162 return c.found 163} 164 165// allocate a context with all its arrays. 166func soe_new(nex: i64) -> *SoeCtx { 167 let c: *SoeCtx = sys_mmap(128) as *SoeCtx 168 c.vecs = sys_mmap(8 * SOE_MAXV * nex) as *i64 169 c.kind = sys_mmap(8 * SOE_MAXV) as *i64 170 c.op = sys_mmap(8 * SOE_MAXV) as *i64 171 c.pa = sys_mmap(8 * SOE_MAXV) as *i64 172 c.pb = sys_mmap(8 * SOE_MAXV) as *i64 173 c.lit = sys_mmap(8 * SOE_MAXV) as *i64 174 c.ht = sys_mmap(8 * SOE_HT) as *i64 175 c.target = sys_mmap(8 * nex) as *i64 176 c.nex = nex 177 return c 178}