code wiki / _hdl_build / nx_food_recipe_mine.nx

nx_food_recipe_mine.nx source

↩ module page · 126 lines · 4960 B

1// nx_food_recipe_mine.nx -- LIB: GROUND the engine's pairings in real recipes (R-RECIPE). Mines the 2// license-clean fetched corpora (Wikibooks/Wikisource/Wikipedia recipe + the Nature food-pairing paper) for 3// INGREDIENT CO-OCCURRENCE -- the food-pairing-hypothesis signal: ingredients that appear together in real 4// recipe/research text pair well. Method: split a corpus into fixed passages; within a passage, mark which 5// KNOWN ingredients (matched by lowercased display name, word-boundary-anchored) are present; every co-present 6// pair scores +1. FACTS-ONLY (only known ingredient tokens are ever recorded, never prose) and LICENSE-GATED 7// (callers pass only fd_source_admissible corpora). Promotes the counts into food:pairlearned:<a>:<b> records, 8// provenance-distinct from the v1 seed (food:pair:*) so learned-vs-seeded stays auditable. license_tier: ORIGINAL 9import "nx_food_science.nx" 10import "nx_food_provenance.nx" 11import "nx_seg_store.nx" 12import "nx_syscalls.nx" 13 14// lowercase ASCII A-Z in place 15func fm_lower(buf: *u8, n: i64) -> i64 { 16 var i: i64 = 0 17 while i < n { 18 let c: i64 = buf[i] as i64 19 if c >= 65 { if c <= 90 { buf[i] = (c + 32) as u8 } } 20 i = i + 1 21 } 22 return 0 23} 24 25// 1 if lowercased term occurs in c[ws..we) anchored at a word boundary (preceding char not a-z). 26// A trailing letter IS allowed (so "onion" matches "onions", "oyster" matches "oysters"). 27func fm_passage_has(c: *u8, ws: i64, we: i64, term: *u8, tl: i64) -> i64 { 28 if tl == 0 { return 0 } 29 var i: i64 = ws 30 while i + tl <= we { 31 var bok: i64 = 1 32 if i > 0 { let pc: i64 = c[i - 1] as i64; if pc >= 97 { if pc <= 122 { bok = 0 } } } 33 if bok == 1 { 34 var k: i64 = 0 35 var m: i64 = 1 36 while k < tl { if c[i + k] != term[k] { m = 0; k = tl } else { k = k + 1 } } 37 if m == 1 { return 1 } 38 } 39 i = i + 1 40 } 41 return 0 42} 43 44// mine one (already-lowercased) corpus into cooc[n*n] (symmetric). termlc[i]/termlen[i] = lowercased name 45// table; `pres` is a caller-owned scratch array of >= n slots (hoisted to avoid per-passage allocation). 46// Returns the number of (passage, ingredient) presence hits observed. 47func fm_mine(world: *i64, c: *u8, clen: i64, window: i64, termlc: *i64, termlen: *i64, cooc: *i64, pres: *i64) -> i64 { 48 let n: i64 = world[0] 49 var hits: i64 = 0 50 var ws: i64 = 0 51 while ws < clen { 52 var we: i64 = ws + window 53 if we > clen { we = clen } 54 var i: i64 = 0 55 while i < n { 56 if fm_passage_has(c, ws, we, termlc[i] as *u8, termlen[i]) == 1 { pres[i] = 1; hits = hits + 1 } else { pres[i] = 0 } 57 i = i + 1 58 } 59 i = 0 60 while i < n { 61 if pres[i] == 1 { 62 var j: i64 = i + 1 63 while j < n { 64 if pres[j] == 1 { cooc[i * n + j] = cooc[i * n + j] + 1; cooc[j * n + i] = cooc[j * n + i] + 1 } 65 j = j + 1 66 } 67 } 68 i = i + 1 69 } 70 ws = ws + window 71 } 72 return hits 73} 74 75func fm_cooc(cooc: *i64, n: i64, i: i64, j: i64) -> i64 { return cooc[i * n + j] } 76 77// build the lowercased match-term table from the world's display names (termlc[i]=*u8, termlen[i]=len) 78func fm_build_terms(world: *i64, termlc: *i64, termlen: *i64) -> i64 { 79 let n: i64 = world[0] 80 let names: *i64 = world[4] as *i64 81 var i: i64 = 0 82 while i < n { 83 let src: *u8 = names[i] as *u8 84 let l: i64 = as_len(src) 85 let cp: *u8 = sys_mmap(l + 4) 86 var k: i64 = 0 87 while k <= l { cp[k] = src[k]; k = k + 1 } 88 fm_lower(cp, l) 89 termlc[i] = cp as i64 90 termlen[i] = l 91 i = i + 1 92 } 93 return 0 94} 95 96// PROMOTE cooc>0 pairs into food:pairlearned:<idA>:<idB> = "<count>" (additive, idempotent skip-if-unchanged). 97// Returns the number of learned-pair records written this pass. 98func fm_promote(prefix: *u8, world: *i64, cooc: *i64) -> i64 { 99 let n: i64 = world[0] 100 let ids: *i64 = world[1] as *i64 101 let w: *i64 = ss_begin() 102 var cnt: i64 = 0 103 var i: i64 = 0 104 while i < n { 105 var j: i64 = i + 1 106 while j < n { 107 let cv: i64 = cooc[i * n + j] 108 if cv > 0 { 109 let key: *u8 = sys_mmap(96) 110 var o: i64 = 0 111 o = as_append(key, o, "food:pairlearned:" as *u8) 112 o = as_append(key, o, ids[i] as *u8) 113 key[o] = 58 as u8; o = o + 1 114 o = as_append(key, o, ids[j] as *u8) 115 key[o] = 0 as u8 116 let val: *u8 = sys_mmap(24) 117 let vl: i64 = fd_apnum(val, 0, cv) 118 if fd_streq_store(prefix, key, val, vl) == 0 { ss_add(w, 1, key, val, vl); cnt = cnt + 1 } 119 } 120 j = j + 1 121 } 122 i = i + 1 123 } 124 if cnt > 0 { let seg: i64 = fd_seg_next(prefix); ss_commit(prefix, w, seg) } 125 return cnt 126}