code wiki / _hdl_build / nx_research_relation.nx
nx_research_relation.nx source
↩ module page · 116 lines · 4319 B
1// nx_research_relation.nx -- Researcher EXTRACT, rung 1 of the GROWN-ORGAN
2// semantic pipeline (permanent intelligence = sovereign Nishi organs, hardware
3// rung up; NO trained weights -- those are benchmark/initial only).
4//
5// The number-only extractor (nx_research_extract) pulls "53" out of "53 percent"
6// -- but misses QUALITATIVE claims (concepts, tradeoffs, architecture), which is
7// the real depth gap vs deep-research. This organ extracts (subject, relation,
8// object) triples by an INSPECTABLE, data-driven cue table -- every decision is
9// visible (which cue matched, the exact spans), the opposite of a black box.
10//
11// API: nx_rel_extract(text, n, subj_out, obj_out) -> relation index (>=0) | -1
12// rel_name(i) -> the relation type string for index i.
13// Reuses re_find / re_strlen from nx_research_extract (DRY). license_tier: ORIGINAL.
14
15import "nx_syscalls.nx"
16import "nx_research_extract.nx"
17
18// ---- relation cue table (DATA: cue phrase -> relation type). Extensible. ----
19func rel_n_cues() -> i64 { return 10 }
20
21func rel_cue(i: i64) -> *u8 {
22 if i == 0 { return " is a " as *u8 }
23 if i == 1 { return " is the " as *u8 }
24 if i == 2 { return " is an " as *u8 }
25 if i == 3 { return " uses " as *u8 }
26 if i == 4 { return " requires " as *u8 }
27 if i == 5 { return " enables " as *u8 }
28 if i == 6 { return " provides " as *u8 }
29 if i == 7 { return " supports " as *u8 }
30 if i == 8 { return " consists of " as *u8 }
31 if i == 9 { return " is based on " as *u8 }
32 return 0 as *u8
33}
34
35func rel_name(i: i64) -> *u8 {
36 if i == 0 { return "IS_A" as *u8 }
37 if i == 1 { return "IS_THE" as *u8 }
38 if i == 2 { return "IS_A" as *u8 }
39 if i == 3 { return "USES" as *u8 }
40 if i == 4 { return "REQUIRES" as *u8 }
41 if i == 5 { return "ENABLES" as *u8 }
42 if i == 6 { return "PROVIDES" as *u8 }
43 if i == 7 { return "SUPPORTS" as *u8 }
44 if i == 8 { return "CONSISTS_OF" as *u8 }
45 if i == 9 { return "BASED_ON" as *u8 }
46 return "NONE" as *u8
47}
48
49// earliest cue in text[0..n): returns cue index | -1; sets *cpos=start, *clen=len.
50func rel_find_cue(text: *u8, n: i64, cpos: *i64, clen: *i64) -> i64 {
51 var best: i64 = 0 - 1
52 var bestpos: i64 = n
53 var bestlen: i64 = 0
54 var i: i64 = 0
55 while i < rel_n_cues() {
56 let cue: *u8 = rel_cue(i)
57 let p: i64 = re_find(text, n, cue)
58 if p >= 0 {
59 if p < bestpos { bestpos = p; best = i; bestlen = re_strlen(cue) }
60 }
61 i = i + 1
62 }
63 if best < 0 { return 0 - 1 }
64 cpos[0] = bestpos
65 clen[0] = bestlen
66 return best
67}
68
69// is byte a clause/sentence stop? ('.' ',' ';' newline)
70func rel_is_stop(c: u8) -> i64 {
71 if c == (46 as u8) { return 1 }
72 if c == (44 as u8) { return 1 }
73 if c == (59 as u8) { return 1 }
74 if c == (10 as u8) { return 1 }
75 return 0
76}
77
78// copy text[a..b) into out, trimming leading/trailing spaces, NUL-terminate; len.
79func rel_slice(text: *u8, a0: i64, b0: i64, out: *u8) -> i64 {
80 var a: i64 = a0
81 var b: i64 = b0
82 while a < b { if text[a] == (32 as u8) { a = a + 1 } else { break } }
83 while b > a { if text[b - 1] == (32 as u8) { b = b - 1 } else { break } }
84 var k: i64 = 0
85 var i: i64 = a
86 while i < b { out[k] = text[i]; k = k + 1; i = i + 1 }
87 out[k] = 0 as u8
88 return k
89}
90
91// Extract ONE (subject, relation, object) claim from a sentence text[0..n).
92// subject = phrase from the last clause-stop before the cue to the cue;
93// object = phrase from the cue end to the next clause-stop (or end).
94func nx_rel_extract(text: *u8, n: i64, subj: *u8, obj: *u8) -> i64 {
95 let cpos: *i64 = (sys_mmap(8)) as *i64
96 let clen: *i64 = (sys_mmap(8)) as *i64
97 let ri: i64 = rel_find_cue(text, n, cpos, clen)
98 if ri < 0 { return 0 - 1 }
99 // subject: walk back from cue start to the last stop (or sentence start)
100 var ss: i64 = cpos[0] - 1
101 while ss > 0 {
102 if rel_is_stop(text[ss]) == 1 { ss = ss + 1; break }
103 ss = ss - 1
104 }
105 if ss < 0 { ss = 0 }
106 rel_slice(text, ss, cpos[0], subj)
107 // object: from cue end forward to the next stop (or n)
108 let oe0: i64 = cpos[0] + clen[0]
109 var oe: i64 = oe0
110 while oe < n {
111 if rel_is_stop(text[oe]) == 1 { break }
112 oe = oe + 1
113 }
114 rel_slice(text, oe0, oe, obj)
115 return ri
116}