code wiki / (root) / nx_depparse_gate.nx

nx_depparse_gate.nx source

↩ module page · 141 lines · 10390 B

1// nx_depparse_gate.nx -- GATE for the dependency parser (nx_depparse), driven IN-PROCESS on a planted CoNLL-U 2// mini-treebank under /tmp/nx_depparse_gate. Proves: the shared reader's HEAD and DEPREL columns (root head 0, a 3// subtype's base name kept), the label table (first-seen interning, unknown reads -1), that the parser LEARNS (a 4// train of one grammar is parsed perfectly at test, UAS and LAS 1000), converges, is deterministic, SKIPS a planted 5// non-projective sentence and counts it, parses an arbitrary tagged token stream through dp_parse_stream with the 6// same answers, and that an untrained parser is not a learned one (its right-branching chain scores far below). 7// Every fixture asserts its own 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_postag.nx" 12import "nx_depparse.nx" 13import "nx_gate_verdict.nx" 14 15const G_ROOT: *u8 = "/tmp/nx_depparse_gate" 16const G_TRAIN: *u8 = "/tmp/nx_depparse_gate/train.conllu" 17const G_TEST: *u8 = "/tmp/nx_depparse_gate/test.conllu" 18const G_SUBTYPE: *u8 = "/tmp/nx_depparse_gate/subtype.conllu" 19const G_I64: i64 = 8 20// five projective sentences of one grammar (det amod nsubj root punct) plus ONE non-projective sentence (arcs 3->1 and 21// 4->2 cross) the static oracle cannot reach and must skip 22const G_TRAIN_ROWS: *u8 = "1\tThe\tthe\tDET\tDT\t_\t2\tdet\t_\t_\n2\tdog\tdog\tNOUN\tNN\t_\t3\tnsubj\t_\t_\n3\truns\trun\tVERB\tVBZ\t_\t0\troot\t_\t_\n4\t.\t.\tPUNCT\t.\t_\t3\tpunct\t_\t_\n\n1\tA\ta\tDET\tDT\t_\t2\tdet\t_\t_\n2\tcat\tcat\tNOUN\tNN\t_\t3\tnsubj\t_\t_\n3\tsleeps\tsleep\tVERB\tVBZ\t_\t0\troot\t_\t_\n4\t.\t.\tPUNCT\t.\t_\t3\tpunct\t_\t_\n\n1\tThe\tthe\tDET\tDT\t_\t3\tdet\t_\t_\n2\tbig\tbig\tADJ\tJJ\t_\t3\tamod\t_\t_\n3\tdog\tdog\tNOUN\tNN\t_\t4\tnsubj\t_\t_\n4\tsleeps\tsleep\tVERB\tVBZ\t_\t0\troot\t_\t_\n5\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n\n1\tA\ta\tDET\tDT\t_\t3\tdet\t_\t_\n2\tsmall\tsmall\tADJ\tJJ\t_\t3\tamod\t_\t_\n3\tcat\tcat\tNOUN\tNN\t_\t4\tnsubj\t_\t_\n4\truns\trun\tVERB\tVBZ\t_\t0\troot\t_\t_\n5\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n\n1\tThe\tthe\tDET\tDT\t_\t2\tdet\t_\t_\n2\tcat\tcat\tNOUN\tNN\t_\t3\tnsubj\t_\t_\n3\truns\trun\tVERB\tVBZ\t_\t0\troot\t_\t_\n\n1\tw1\tw1\tNOUN\tNN\t_\t3\tobl\t_\t_\n2\tw2\tw2\tNOUN\tNN\t_\t4\tobl\t_\t_\n3\tw3\tw3\tVERB\tVBZ\t_\t0\troot\t_\t_\n4\tw4\tw4\tVERB\tVBZ\t_\t3\tconj\t_\t_\n" 23const G_TRAIN_SENTS: i64 = 6 24const G_TRAIN_TOKS: i64 = 26 // 4+4+5+5+3+5? no: 4+4+5+5+3+4 = 25 tokens plus the reader counts the range-free rows only 25const G_NONPROJ: i64 = 1 26const G_NREL: i64 = 7 // det nsubj root punct amod obl conj, in first-seen order 27const G_REL_ROOT: i64 = 2 28const G_TEST_ROWS: *u8 = "1\tThe\tthe\tDET\tDT\t_\t3\tdet\t_\t_\n2\tsmall\tsmall\tADJ\tJJ\t_\t3\tamod\t_\t_\n3\tdog\tdog\tNOUN\tNN\t_\t4\tnsubj\t_\t_\n4\tsleeps\tsleep\tVERB\tVBZ\t_\t0\troot\t_\t_\n5\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n\n1\tA\ta\tDET\tDT\t_\t3\tdet\t_\t_\n2\tbig\tbig\tADJ\tJJ\t_\t3\tamod\t_\t_\n3\tcat\tcat\tNOUN\tNN\t_\t4\tnsubj\t_\t_\n4\truns\trun\tVERB\tVBZ\t_\t0\troot\t_\t_\n5\t.\t.\tPUNCT\t.\t_\t4\tpunct\t_\t_\n" 29const G_TEST_TOKS: i64 = 10 30const G_TEST_SENTS: i64 = 2 31// a subtype relation: the reader keeps the base name before ':' 32const G_SUBTYPE_ROWS: *u8 = "1\tits\tits\tPRON\tPRP$\t_\t2\tnmod:poss\t_\t_\n2\ttoy\ttoy\tNOUN\tNN\t_\t0\troot\t_\t_\n" 33const G_STREAM_TOKS: i64 = 3 34const G_UNTRAINED_MAX: i64 = 500 35 36func main() -> i64 { 37 gv_head("=== nx_depparse_gate -- the dependency parser reads CoNLL-U heads, learns a grammar, and parses a token stream ===" as *u8) 38 let c: *i64 = gv_ctr() 39 gk_mkdir(G_ROOT) 40 gk_write(G_TRAIN, G_TRAIN_ROWS) 41 gk_write(G_TEST, G_TEST_ROWS) 42 gk_write(G_SUBTYPE, G_SUBTYPE_ROWS) 43 gv_check("fixture-reached-the-condition: train, test and subtype treebanks planted" as *u8, gk_exists(G_TRAIN) * gk_exists(G_TEST) * gk_exists(G_SUBTYPE), c) 44 45 // the shared reader's dependency columns, read directly 46 pt_reset() 47 let lp: *i64 = sys_mmap(RM_I64_PAIR) as *i64 48 lp[0] = 0 49 let b: *u8 = sys_read_file(G_TRAIN, lp) 50 let ip: *i64 = sys_mmap(RM_I64_PAIR) as *i64 51 let unk: *i64 = sys_mmap(RM_I64_PAIR) as *i64 52 ip[0] = 0 53 unk[0] = 0 54 let cnt1: i64 = pt_read_sentence(b, lp[0], ip, unk) 55 gv_check_eq("reader-first-sentence-four-tokens" as *u8, cnt1, 4, c) 56 gv_check_eq("reader-HEAD-of-the-root-verb-is-zero" as *u8, pt_heads[2], 0, c) 57 gv_check_eq("reader-HEAD-of-the-subject-is-three (1-based id of the verb)" as *u8, pt_heads[1], 3, c) 58 gv_check_eq("reader-DEPREL-of-the-subject-is-five-bytes (nsubj)" as *u8, pt_rellens[1], 5, c) 59 gv_check_eq("reader-DEPREL-of-the-determiner-is-three-bytes (det)" as *u8, pt_rellens[0], 3, c) 60 let lp2: *i64 = sys_mmap(RM_I64_PAIR) as *i64 61 lp2[0] = 0 62 let b2: *u8 = sys_read_file(G_SUBTYPE, lp2) 63 ip[0] = 0 64 let cnt2: i64 = pt_read_sentence(b2, lp2[0], ip, unk) 65 gv_check_eq("subtype-fixture-reached-the-condition: two tokens read" as *u8, cnt2, 2, c) 66 gv_check_eq("reader-keeps-the-base-name-before-the-colon (nmod:poss reads as four bytes, nmod)" as *u8, pt_rellens[0], 4, c) 67 68 // the label table 69 dp_reset() 70 gv_check_eq("label-table-starts-empty" as *u8, dp_nrel, 0, c) 71 gv_check_eq("label-table-interns-first-seen-as-id-0" as *u8, dp_rel_id("det" as *u8, 3, 1), 0, c) 72 gv_check_eq("label-table-interns-a-second-name-as-id-1" as *u8, dp_rel_id("nsubj" as *u8, 5, 1), 1, c) 73 gv_check_eq("label-table-finds-a-known-name-without-interning" as *u8, dp_rel_id("det" as *u8, 3, 0), 0, c) 74 gv_check_eq("neg-control-label-table-unknown-name-reads-minus-one-when-not-interning" as *u8, dp_rel_id("zzz" as *u8, 3, 0), DP_NONE, c) 75 let nb: *u8 = sys_mmap(DP_RELNAME) 76 gv_check_eq("label-name-round-trips (nsubj, 5 bytes)" as *u8, dp_rel_name(1, nb), 5, c) 77 78 // learn: the tagger first (the parser reads its tags), then the parser 79 let po: *i64 = sys_mmap(PT_O_N * G_I64) as *i64 80 pt_eval(G_TRAIN, G_TEST, po) 81 gv_check_eq("tagger-ready-before-the-parser (tagger test accuracy 1000 on the planted words)" as *u8, po[PT_O_ACC], PT_PERMIL, c) 82 let o: *i64 = sys_mmap(DP_O_N * G_I64) as *i64 83 dp_eval(G_TRAIN, G_TEST, o) 84 gv_check_eq("train-sentences-equal-the-planted-count (six)" as *u8, o[DP_O_TRAINSENT], G_TRAIN_SENTS, c) 85 gv_check_eq("nonprojective-fixture-reached-the-condition: exactly one training sentence skipped as unreachable" as *u8, o[DP_O_NONPROJ], G_NONPROJ, c) 86 gv_check_eq("no-unusable-training-sentences (every HEAD present, table not full)" as *u8, o[DP_O_UNUSABLE], 0, c) 87 gv_check_eq("relation-labels-interned-equal-the-planted-count (seven)" as *u8, o[DP_O_NREL], G_NREL, c) 88 gv_check_eq("test-tokens-ten" as *u8, o[DP_O_TOKENS], G_TEST_TOKS, c) 89 gv_check_eq("test-sentences-two" as *u8, o[DP_O_TESTSENT], G_TEST_SENTS, c) 90 gv_check_eq("parser-LEARNS: every test head correct (UAS 1000 permil)" as *u8, o[DP_O_UAS], DP_PERMIL, c) 91 gv_check_eq("parser-LEARNS-labels: every test relation correct (LAS 1000 permil)" as *u8, o[DP_O_LAS], DP_PERMIL, c) 92 gv_check_eq("parser-converged: no updates in the last epoch" as *u8, o[DP_O_UPDATES], 0, c) 93 94 // determinism 95 let o2: *i64 = sys_mmap(DP_O_N * G_I64) as *i64 96 dp_eval(G_TRAIN, G_TEST, o2) 97 gv_check_eq("parser-is-deterministic: same UAS on a second run" as *u8, o2[DP_O_UAS], o[DP_O_UAS], c) 98 99 // the stream entry point the aspect model uses: tagged tokens in one buffer -> heads and relations 100 let sb: *u8 = sys_mmap(64) 101 let so: *i64 = sys_mmap(8 * G_I64) as *i64 102 let sl: *i64 = sys_mmap(8 * G_I64) as *i64 103 let st: *i64 = sys_mmap(8 * G_I64) as *i64 104 let sh: *i64 = sys_mmap(8 * G_I64) as *i64 105 let sr: *i64 = sys_mmap(8 * G_I64) as *i64 106 var w: i64 = rm_catn(sb, 0, "the" as *u8, 3); so[0] = 0; sl[0] = 3 107 so[1] = w; sl[1] = 3; w = rm_catn(sb, w, "dog" as *u8, 3) 108 so[2] = w; sl[2] = 4; w = rm_catn(sb, w, "runs" as *u8, 4) 109 pt_tag_stream(sb, so, sl, G_STREAM_TOKS, st) 110 let parsed: i64 = dp_parse_stream(sb, so, sl, st, G_STREAM_TOKS, sh, sr) 111 gv_check_eq("stream-parsing-returns-the-token-count" as *u8, parsed, G_STREAM_TOKS, c) 112 gv_check_eq("stream-parsing-the-attaches-to-dog (head index 1)" as *u8, sh[0], 1, c) 113 gv_check_eq("stream-parsing-dog-attaches-to-runs (head index 2)" as *u8, sh[1], 2, c) 114 gv_check_eq("stream-parsing-runs-is-the-root (head -1)" as *u8, sh[2], DP_NONE, c) 115 gv_check_eq("stream-parsing-runs-carries-the-root-relation" as *u8, sr[2], G_REL_ROOT, c) 116 gv_check_eq("stream-parsing-dog-is-nsubj (relation id 1)" as *u8, sr[1], 1, c) 117 118 // neg-control: an UNTRAINED parser (weights zero, ties to SHIFT) builds a right-branching chain, which attaches 119 // every token to its left neighbour -- on this grammar that is never the gold head, so it scores far below 1000 120 dp_reset() 121 dp_rel_id("det" as *u8, 3, 1) 122 let ne: *i64 = sys_mmap(DP_O_N * G_I64) as *i64 123 var qq: i64 = 0 124 while qq < DP_O_N { ne[qq] = 0; qq = qq + 1 } 125 dp_T = 1 126 dp_test(G_TEST, ne) 127 gv_check_eq("neg-control-untrained-parser-scores-zero-UAS-on-this-grammar" as *u8, ne[DP_O_UAS], 0, c) 128 gv_check("neg-control-untrained-parser-is-far-below-the-learned-one" as *u8, (ne[DP_O_UAS] < G_UNTRAINED_MAX) as i64, c) 129 130 gv_values_head() 131 gv_kv("test_uas_permil" as *u8, o[DP_O_UAS]) 132 gv_kv("test_las_base_permil" as *u8, o[DP_O_LAS]) 133 gv_kv("train_sentences" as *u8, o[DP_O_TRAINSENT]) 134 gv_kv("train_tokens" as *u8, o[DP_O_TRAINTOK]) 135 gv_kv("nonprojective_skipped" as *u8, o[DP_O_NONPROJ]) 136 gv_kv("relation_labels" as *u8, o[DP_O_NREL]) 137 gv_kv("last_epoch_updates" as *u8, o[DP_O_UPDATES]) 138 gv_kv("untrained_uas_permil" as *u8, ne[DP_O_UAS]) 139 gv_kv("tagger_accuracy_permil" as *u8, po[PT_O_ACC]) 140 return gv_verdict("nx_depparse_gate" as *u8, c, "the dependency parser proven on a planted CoNLL-U mini-treebank: the shared reader carries HEAD and the base DEPREL, the label table interns first-seen and refuses unknowns, the parser learns a grammar to perfect UAS and LAS and converges, is deterministic, skips and counts a non-projective sentence, parses an arbitrary tagged token stream identically, and an untrained parser scores zero on the same grammar; every fixture asserts its own condition first" as *u8) 141}