code wiki / _hdl_build / nx_survey_sentiment_gate.nx

nx_survey_sentiment_gate.nx source

↩ module page · 108 lines · 6787 B

1import "nx_gate_gn.nx" 2// nx_survey_sentiment_gate.nx -- GATE for the sovereign open-text sentiment scorer. Every compound score is 3// checked against a HAND-COMPUTED known answer (the VADER-style integer formula S/sqrt(S^2+15e6) in permille), 4// and the ADVERSARIAL controls prove the two things a naive bag-of-words gets WRONG: negation ("not helpful" 5// must score NEGATIVE, not positive) and mixed answers (must net near-neutral, not falsely strong). A live 6// aggregate over a hermetic /tmp store proves the end-to-end path. Exits 0 iff ALL pass. 7// license_tier: ORIGINAL expect_exit: 0 8import "nx_syscalls.nx" 9import "nx_survey_sentiment.nx" 10 11func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 12func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 { 13 if got == want { st[0] = st[0] + 1; gp(" PASS " as *u8); gp(name); gp("\n" as *u8) } 14 else { st[1] = st[1] + 1; gp(" FAIL " as *u8); gp(name); gp(" got=" as *u8); gn(got); gp(" want=" as *u8); gn(want); gp("\n" as *u8) } 15 return 0 16} 17// score a plain string convenience wrapper 18func sc(words: *i64, kinds: *i64, vals: *i64, n: i64, s: *u8) -> i64 { 19 return sn_score(words, kinds, vals, n, s, se_slen(s)) 20} 21 22func main() -> i64 { 23 let st: *i64 = sys_mmap(16) as *i64 24 st[0] = 0 25 st[1] = 0 26 27 // ---- load the embedded lexicon ---- 28 let words: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64 29 let kinds: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64 30 let vals: *i64 = sys_mmap(8 * SN_MAXLEX) as *i64 31 let nlex: i64 = sn_load(SN_LEX, words, kinds, vals, SN_MAXLEX) 32 var lex_ok: i64 = 0 33 if nlex > 100 { if nlex < SN_MAXLEX { lex_ok = 1 } } 34 chk("V1 lexicon loads (>100 entries, under cap)" as *u8, lex_ok, 1, st) 35 // spot-check the parse of a NEGATIVE valence written "0 - 2100" 36 let bi: i64 = sn_find(words, nlex, "bad" as *u8) 37 var badneg: i64 = 0 38 if bi >= 0 { if vals[bi] == (0 - 2100) { if kinds[bi] == 119 { badneg = 1 } } } 39 chk("V1b 'bad' parsed as w / valence -2100 (the 0 - N literal path)" as *u8, badneg, 1, st) 40 let ni: i64 = sn_find(words, nlex, "not" as *u8) 41 var negkind: i64 = 0 42 if ni >= 0 { if kinds[ni] == 110 { negkind = 1 } } 43 chk("V1c 'not' parsed as negation kind" as *u8, negkind, 1, st) 44 45 // ---- KAT compounds (hand-computed: compound_permille = 1000*S / isqrt(S*S + 15000000)) ---- 46 // "very helpful"(+2586) + "kind"(+2000) -> S=4586 -> 4586000/6002 = 764 47 chk("V2 'the staff were very helpful and kind' = +764" as *u8, sc(words, kinds, vals, nlex, "the staff were very helpful and kind" as *u8), 764, st) 48 // NEGATION: "not helpful"(-1480) -> S=-1480 -> -1480000/4146 = -356 (a naive scorer would say +458) 49 chk("V3 NEGATION 'not helpful at all' = -356 (flipped, not positive)" as *u8, sc(words, kinds, vals, nlex, "not helpful at all" as *u8), 0 - 356, st) 50 chk("V3b that classifies NEGATIVE" as *u8, sn_class(sc(words, kinds, vals, nlex, "not helpful at all" as *u8)), 0 - 1, st) 51 // neutral: no lexicon words 52 chk("V4 'the meeting is at noon' = 0 (neutral, no affect words)" as *u8, sc(words, kinds, vals, nlex, "the meeting is at noon" as *u8), 0, st) 53 chk("V4b classifies NEUTRAL" as *u8, sn_class(sc(words, kinds, vals, nlex, "the meeting is at noon" as *u8)), 0, st) 54 // intensified negative: "so worried"(-2586) + "stressed"(-2000) -> S=-4586 -> -764 55 chk("V5 'i am so worried and stressed about the rent' = -764" as *u8, sc(words, kinds, vals, nlex, "i am so worried and stressed about the rent" as *u8), 0 - 764, st) 56 // ---- ADVERSARY: intensifier must INCREASE magnitude ---- 57 // "helpful"(2000) -> 2000000/4358 = 458 ; "very helpful"(2586) -> 2586000/4656 = 555 58 let plain: i64 = sc(words, kinds, vals, nlex, "helpful" as *u8) 59 let inten: i64 = sc(words, kinds, vals, nlex, "very helpful" as *u8) 60 chk("V6 'helpful' = +458" as *u8, plain, 458, st) 61 chk("V6b 'very helpful' = +555 (intensifier amplified)" as *u8, inten, 555, st) 62 var amp: i64 = 0 63 if inten > plain { amp = 1 } 64 chk("V6c very-helpful STRICTLY stronger than helpful" as *u8, amp, 1, st) 65 // ---- ADVERSARY: a genuinely MIXED answer nets near-neutral, not falsely strong ---- 66 // "good"(1900) + "difficult"(-1800) -> S=100 -> 100000/3874 = 25 -> neutral 67 chk("V7 MIXED 'good food but a difficult month' = +25" as *u8, sc(words, kinds, vals, nlex, "good food but a difficult month" as *u8), 25, st) 68 chk("V7b mixed answer classifies NEUTRAL (not falsely strong)" as *u8, sn_class(sc(words, kinds, vals, nlex, "good food but a difficult month" as *u8)), 0, st) 69 // ---- ADVERSARY: gibberish / empty -> 0 ---- 70 chk("V8 gibberish 'asdf qwer zxcv' = 0" as *u8, sc(words, kinds, vals, nlex, "asdf qwer zxcv" as *u8), 0, st) 71 chk("V8b empty string = 0" as *u8, sc(words, kinds, vals, nlex, "" as *u8), 0, st) 72 73 // ---- AGGREGATE end-to-end over a hermetic /tmp store ---- 74 let pfx: *u8 = sys_mmap(64) 75 var pp: i64 = se_cat(pfx, 0, "/tmp/svys_" as *u8) 76 pp = se_catn(pfx, pp, sys_now_us()) 77 pp = se_catc(pfx, pp, 45) 78 pfx[pp] = 0 as u8 79 let idb: *u8 = sys_mmap(64) 80 let spec: *u8 = "@survey feel\n@title How are things\nQ|c1|T|In a sentence, how are things going?\n" as *u8 81 chk("V9 load 1 text-question survey" as *u8, se_load(pfx, spec, se_slen(spec), idb, 64), 1, st) 82 let aq: *i64 = sys_mmap(8 * 4) as *i64 83 let av: *i64 = sys_mmap(8 * 4) as *i64 84 aq[0] = "c1" as *u8 as i64 85 av[0] = "the staff were very helpful and kind" as *u8 as i64 86 chk("V9a positive answer recorded" as *u8, se_respond(pfx, "feel" as *u8, "aa-tok0" as *u8, aq, av, 1), 1, st) 87 av[0] = "not helpful at all" as *u8 as i64 88 chk("V9b negative answer recorded" as *u8, se_respond(pfx, "feel" as *u8, "bb-tok0" as *u8, aq, av, 1), 1, st) 89 av[0] = "the meeting is at noon" as *u8 as i64 90 chk("V9c neutral answer recorded" as *u8, se_respond(pfx, "feel" as *u8, "cc-tok0" as *u8, aq, av, 1), 1, st) 91 let out: *i64 = sys_mmap(8 * 8) as *i64 92 let nsc: i64 = sn_aggregate(pfx, "feel" as *u8, "c1" as *u8, words, kinds, vals, nlex, out) 93 chk("V10 aggregate scored n=3" as *u8, nsc, 3, st) 94 chk("V10a positives=1" as *u8, out[2], 1, st) 95 chk("V10b neutrals=1" as *u8, out[3], 1, st) 96 chk("V10c negatives=1" as *u8, out[4], 1, st) 97 // mean = (764 + (-356) + 0)/3 = 408/3 = 136 98 chk("V10d mean compound = +136 permille" as *u8, out[1], 136, st) 99 100 gp("nx_survey_sentiment_gate: PASS=" as *u8) 101 gn(st[0]) 102 gp(" FAIL=" as *u8) 103 gn(st[1]) 104 gp("\n" as *u8) 105 if st[1] == 0 { gp("VERDICT: GREEN (VADER-style integer sentiment: lexicon + negation-flip + intensifier + compound norm, KAT-exact, adversary-guarded)\n" as *u8); return 0 } 106 gp("VERDICT: RED\n" as *u8) 107 return 1 108}