code wiki / (root) / nx_wordclust_gate.nx

nx_wordclust_gate.nx source

↩ module page · 118 lines · 7569 B

1// nx_wordclust_gate.nx -- GATE for the PPMI word clusterer (nx_wordclust), driven IN-PROCESS on a PLANTED NXPPMI1 model 2// under /tmp/nx_wordclust_gate (six words, three orthogonal context vectors, written by the library's own fixture 3// writer through the ONE loader's format). Proves: the fixture is read back by the canonical loader, words the model 4// knows resolve and one it does not reads -1, k-medoids at K=3 groups the three synonym pairs and separates the three 5// pairs, a word unseen at build time is assigned on demand to its pair's cluster, ids spell as two bytes and -1 as one, 6// two builds agree, and a missing model fails closed (no clusters, never a crash). Every fixture asserts its own 7// condition first. No network. license_tier: ORIGINAL No hw writes (Rule 26). 8import "nx_syscalls.nx" 9import "nx_gatekit_lib.nx" 10import "nx_reviewmine_lib.nx" 11import "nx_wordclust.nx" 12import "nx_gate_verdict.nx" 13 14const G_ROOT: *u8 = "/tmp/nx_wordclust_gate" 15const G_MODEL: *u8 = "/tmp/nx_wordclust_gate/mini.ppmi" 16const G_MISSING: *u8 = "/tmp/nx_wordclust_gate/absent.ppmi" 17const G_I64: i64 = 8 18const G_NW: i64 = 6 19const G_MODEL_BYTES: i64 = 280 // 32 + 6*8 + 7*8 + 6*8 + 6*8 + 6*8 20const G_K: i64 = 3 21const G_BUILD_WORDS: i64 = 5 // food is held back and assigned on demand 22 23func main() -> i64 { 24 gv_head("=== nx_wordclust_gate -- PPMI word clusters group synonyms and separate the rest on a planted model ===" as *u8) 25 let c: *i64 = gv_ctr() 26 gk_mkdir(G_ROOT) 27 // the planted vocabulary: battery~charger (context 0), screen~display (context 1), waiter~food (context 2) 28 let wb: *u8 = sys_mmap(128) 29 let wo: *i64 = sys_mmap(G_NW * G_I64) as *i64 30 let wl: *i64 = sys_mmap(G_NW * G_I64) as *i64 31 let wx: *i64 = sys_mmap(G_NW * G_I64) as *i64 32 var w: i64 = 0 33 wo[0] = w; wl[0] = 7; wx[0] = 0; w = rm_catn(wb, w, "battery" as *u8, 7) 34 wo[1] = w; wl[1] = 7; wx[1] = 0; w = rm_catn(wb, w, "charger" as *u8, 7) 35 wo[2] = w; wl[2] = 6; wx[2] = 1; w = rm_catn(wb, w, "screen" as *u8, 6) 36 wo[3] = w; wl[3] = 7; wx[3] = 1; w = rm_catn(wb, w, "display" as *u8, 7) 37 wo[4] = w; wl[4] = 6; wx[4] = 2; w = rm_catn(wb, w, "waiter" as *u8, 6) 38 wo[5] = w; wl[5] = 4; wx[5] = 2; w = rm_catn(wb, w, "food" as *u8, 4) 39 let planted: i64 = wc_plant_model(G_MODEL, wb, wo, wl, wx, G_NW) 40 gv_check_eq("fixture-reached-the-condition: the planted model was written" as *u8, planted, 1, c) 41 gv_check_eq("fixture-reached-the-condition: the planted model has the format's exact byte count" as *u8, gk_size(G_MODEL), G_MODEL_BYTES, c) 42 43 wc_reset() 44 gv_check_eq("the-canonical-loader-reads-the-planted-model" as *u8, wc_load_model(G_MODEL), 1, c) 45 wc_set_k(G_K, G_K, G_K) 46 // frequencies seed the medoids: one word per pair leads, the pairs' partners trail, an unknown word is added too 47 var i: i64 = 0 48 while i < 5 { wc_add("battery" as *u8, 7); i = i + 1 } 49 i = 0 50 while i < 4 { wc_add("screen" as *u8, 6); i = i + 1 } 51 i = 0 52 while i < 3 { wc_add("waiter" as *u8, 6); i = i + 1 } 53 wc_add("charger" as *u8, 7) 54 wc_add("display" as *u8, 7) 55 wc_add("zzzq" as *u8, 4) 56 let o: *i64 = sys_mmap(WC_O_N * G_I64) as *i64 57 let built: i64 = wc_build(o) 58 gv_check_eq("build-returns-one" as *u8, built, 1, c) 59 gv_check_eq("vocabulary-added-six (five in the model plus one the model does not know)" as *u8, o[WC_O_VOCAB], G_BUILD_WORDS + 1, c) 60 gv_check_eq("words-the-model-knows-five" as *u8, o[WC_O_INMODEL], G_BUILD_WORDS, c) 61 gv_check_eq("medoids-used-equal-K-at-level-0" as *u8, o[WC_O_K0], G_K, c) 62 gv_check_eq("medoids-used-equal-K-at-level-2" as *u8, o[WC_O_K2], G_K, c) 63 let cb: i64 = wc_cluster("battery" as *u8, 7, 0) 64 let cc: i64 = wc_cluster("charger" as *u8, 7, 0) 65 let cs: i64 = wc_cluster("screen" as *u8, 6, 0) 66 let cd: i64 = wc_cluster("display" as *u8, 7, 0) 67 let cw: i64 = wc_cluster("waiter" as *u8, 6, 0) 68 gv_check("every-known-word-has-a-cluster (ids 0..K-1)" as *u8, ((cb >= 0) & (cc >= 0) & (cs >= 0) & (cd >= 0) & (cw >= 0) & (cb < G_K) & (cs < G_K) & (cw < G_K)) as i64, c) 69 gv_check_eq("synonyms-share-a-cluster: battery and charger" as *u8, cc, cb, c) 70 gv_check_eq("synonyms-share-a-cluster: screen and display" as *u8, cd, cs, c) 71 gv_check("different-pairs-are-separated: battery, screen and waiter in three clusters" as *u8, ((cb != cs) & (cs != cw) & (cb != cw)) as i64, c) 72 gv_check_eq("neg-control-a-word-the-model-does-not-know-reads-minus-one" as *u8, wc_cluster("zzzq" as *u8, 4, 0), WC_NONE, c) 73 // on-demand assignment: food was never added at build time, its vector matches waiter's context 74 let cf: i64 = wc_cluster("food" as *u8, 4, 0) 75 gv_check_eq("unseen-word-assigned-on-demand-to-its-pair's-cluster (food with waiter)" as *u8, cf, cw, c) 76 gv_check_eq("on-demand-assignment-is-cached (a second lookup answers the same id)" as *u8, wc_cluster("food" as *u8, 4, 0), cf, c) 77 gv_check_eq("case-folds-at-lookup (Battery reads battery)" as *u8, wc_cluster("Battery" as *u8, 7, 0), cb, c) 78 // spelling 79 let sb: *u8 = sys_mmap(4) 80 gv_check_eq("spell-a-cluster-id-as-two-bytes" as *u8, wc_spell(65, sb), 2, c) 81 gv_check_eq("spell-two-bytes-high-digit (65 / 64 = 1)" as *u8, sb[0] as i64, 49, c) 82 gv_check_eq("spell-two-bytes-low-digit (65 mod 64 = 1)" as *u8, sb[1] as i64, 49, c) 83 gv_check_eq("spell-minus-one-as-one-byte" as *u8, wc_spell(WC_NONE, sb), 1, c) 84 // determinism: a second reset+build gives the same ids 85 wc_reset() 86 wc_load_model(G_MODEL) 87 wc_set_k(G_K, G_K, G_K) 88 i = 0 89 while i < 5 { wc_add("battery" as *u8, 7); i = i + 1 } 90 i = 0 91 while i < 4 { wc_add("screen" as *u8, 6); i = i + 1 } 92 i = 0 93 while i < 3 { wc_add("waiter" as *u8, 6); i = i + 1 } 94 wc_add("charger" as *u8, 7) 95 wc_add("display" as *u8, 7) 96 wc_add("zzzq" as *u8, 4) 97 let o2: *i64 = sys_mmap(WC_O_N * G_I64) as *i64 98 wc_build(o2) 99 gv_check_eq("clustering-is-deterministic (battery's id agrees across two builds)" as *u8, wc_cluster("battery" as *u8, 7, 0), cb, c) 100 gv_check_eq("clustering-is-deterministic (screen's id agrees across two builds)" as *u8, wc_cluster("screen" as *u8, 6, 0), cs, c) 101 // neg-control: a missing model fails closed 102 wc_reset() 103 gv_check_eq("neg-control-missing-model-refuses-to-load" as *u8, wc_load_model(G_MISSING), 0, c) 104 wc_add("battery" as *u8, 7) 105 let o3: *i64 = sys_mmap(WC_O_N * G_I64) as *i64 106 gv_check_eq("neg-control-build-without-a-model-returns-zero" as *u8, wc_build(o3), 0, c) 107 gv_check_eq("neg-control-no-clusters-without-a-model (battery reads minus one)" as *u8, wc_cluster("battery" as *u8, 7, 0), WC_NONE, c) 108 109 gv_values_head() 110 gv_kv("vocab" as *u8, o[WC_O_VOCAB]) 111 gv_kv("in_model" as *u8, o[WC_O_INMODEL]) 112 gv_kv("k0" as *u8, o[WC_O_K0]) 113 gv_kv("cluster_battery" as *u8, cb) 114 gv_kv("cluster_screen" as *u8, cs) 115 gv_kv("cluster_waiter" as *u8, cw) 116 gv_kv("cluster_food_on_demand" as *u8, cf) 117 return gv_verdict("nx_wordclust_gate" as *u8, c, "the PPMI word clusterer proven on a planted six-word model written in the canonical loader's format: the loader reads it, known words resolve and an unknown one reads -1, k-medoids at K=3 groups each synonym pair and separates the pairs, an unseen word is assigned on demand to its pair and cached, lookups case-fold, ids spell as two bytes and -1 as one, two builds agree, and a missing model fails closed with no clusters; every fixture asserts its own condition first" as *u8) 118}