code wiki / (root) / nx_blockmax_wand.nx

nx_blockmax_wand.nx source

↩ module page · 79 lines · 4426 B

1// nx_blockmax_wand.nx -- LIB: block-max top-k pruning (block-max-WAND / MaxScore family) = the QUERY-SPEED lever for a 2// billion-posting index. Instead of scoring every doc in every query term's postings, keep a top-k threshold and PRUNE 3// any doc whose UPPER BOUND (sum of the per-BLOCK max impacts of the terms that contain it) can't beat the threshold. 4// Composes the 256-doc blocks from nx_posting_compress -- each block carries a max-impact, so whole blocks skip. The 5// invariant that makes it EXACT: upper_bound >= true score, so a pruned doc could never have entered the top-k. 6// Built + gated NOW on a fixture (exactness vs exhaustive + measured skips); the pivot/skip-pointer scaling runs on the 7// NAS index later. No float. license_tier: ORIGINAL 8import "nx_syscalls.nx" 9 10const BMW_B: i64 = 2 // demo block size (fine, so block-max < term-max); production block = 256 (nx_posting_compress) 11 12// per-block max impact for a term's impact array -> bm[]; returns block count. 13func bmw_block_max(imp: *i64, cnt: i64, bm: *i64) -> i64 { 14 var b: i64 = 0; var i: i64 = 0 15 while i < cnt { 16 var mx: i64 = 0; var j: i64 = 0 17 while j < BMW_B { if i+j < cnt { if imp[i+j] > mx { mx = imp[i+j] } } j = j + 1 } 18 bm[b] = mx; b = b + 1; i = i + BMW_B 19 } 20 return b 21} 22// impact of a term (docs,imp,cnt) for doc d; sets blk[0]=block index; returns -1 if absent. 23func bmw_find(docs: *i64, imp: *i64, cnt: i64, d: i64, blk: *i64) -> i64 { 24 var i: i64 = 0 25 while i < cnt { if docs[i] == d { blk[0] = i / BMW_B; return imp[i] } i = i + 1 } 26 return 0 - 1 27} 28// full score of doc d = sum of impacts of query terms containing d. 29func bmw_score(td: *i64, ti: *i64, tc: *i64, nt: i64, d: i64) -> i64 { 30 var s: i64 = 0; var t: i64 = 0 31 let blk: *i64 = sys_mmap(8) as *i64 32 while t < nt { let im: i64 = bmw_find(td[t] as *i64, ti[t] as *i64, tc[t], d, blk); if im >= 0 { s = s + im } t = t + 1 } 33 return s 34} 35// block-max upper bound for d = sum of per-block max impacts of the terms containing d (>= true score). 36func bmw_ub(td: *i64, ti: *i64, tc: *i64, tbm: *i64, nt: i64, d: i64) -> i64 { 37 var ub: i64 = 0; var t: i64 = 0 38 let blk: *i64 = sys_mmap(8) as *i64 39 while t < nt { let im: i64 = bmw_find(td[t] as *i64, ti[t] as *i64, tc[t], d, blk); if im >= 0 { let bm: *i64 = tbm[t] as *i64; ub = ub + bm[blk[0]] } t = t + 1 } 40 return ub 41} 42// insert (d,s) into the top-k arrays; maintain theta[0] = current kth-best (min of the k). returns 1 if inserted. 43func bmw_insert(tkd: *i64, tks: *i64, k: i64, filled: *i64, theta: *i64, d: i64, s: i64) -> i64 { 44 if filled[0] < k { 45 tkd[filled[0]] = d; tks[filled[0]] = s; filled[0] = filled[0] + 1 46 if filled[0] == k { var mn: i64 = tks[0]; var i: i64 = 1; while i < k { if tks[i] < mn { mn = tks[i] } i = i + 1 } theta[0] = mn } 47 return 1 48 } 49 if s > theta[0] { 50 var mi: i64 = 0; var i: i64 = 1; while i < k { if tks[i] < tks[mi] { mi = i } i = i + 1 } 51 tkd[mi] = d; tks[mi] = s 52 var mn: i64 = tks[0]; i = 1; while i < k { if tks[i] < mn { mn = tks[i] } i = i + 1 } theta[0] = mn 53 return 1 54 } 55 return 0 56} 57// EXHAUSTIVE top-k: score every union doc. fills tkd/tks[k]; returns docs scored (= nu). 58func bmw_exhaustive(td: *i64, ti: *i64, tc: *i64, nt: i64, udocs: *i64, nu: i64, k: i64, tkd: *i64, tks: *i64) -> i64 { 59 let filled: *i64 = sys_mmap(8) as *i64; filled[0] = 0 60 let theta: *i64 = sys_mmap(8) as *i64; theta[0] = 0 61 var i: i64 = 0 62 while i < nu { let s: i64 = bmw_score(td, ti, tc, nt, udocs[i]); bmw_insert(tkd, tks, k, filled, theta, udocs[i], s); i = i + 1 } 63 return nu 64} 65// BLOCK-MAX PRUNED top-k: skip any doc whose block-max upper bound <= threshold. fills tkd/tks[k]; returns docs FULLY SCORED. 66func bmw_blockmax(td: *i64, ti: *i64, tc: *i64, tbm: *i64, nt: i64, udocs: *i64, nu: i64, k: i64, tkd: *i64, tks: *i64) -> i64 { 67 let filled: *i64 = sys_mmap(8) as *i64; filled[0] = 0 68 let theta: *i64 = sys_mmap(8) as *i64; theta[0] = 0 69 var scored: i64 = 0 70 var i: i64 = 0 71 while i < nu { 72 let d: i64 = udocs[i] 73 var doit: i64 = 0 74 if filled[0] < k { doit = 1 } else { if bmw_ub(td, ti, tc, tbm, nt, d) > theta[0] { doit = 1 } } 75 if doit == 1 { let s: i64 = bmw_score(td, ti, tc, nt, d); scored = scored + 1; bmw_insert(tkd, tks, k, filled, theta, d, s) } 76 i = i + 1 77 } 78 return scored 79}