SNAPKITTYWEST commited on
Commit
ce0d85d
Β·
verified Β·
1 Parent(s): b47f786

push from SNAPKITTYWEST/pocketlearn

Browse files
LICENSE.tri ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TRI-LICENSE STRUCTURE
2
+ =====================
3
+
4
+ This project is available under THREE licensing options:
5
+
6
+ 1. Business Source License 1.1 (BSL-1.1)
7
+ - Source-available with commercial restrictions
8
+ - No managed service offerings at enterprise scale
9
+ - Converts to AGPL-3.0 after transition period (Change Date: 2028-08-08)
10
+ - Full text: LICENSE.BSL
11
+
12
+ 2. GNU Affero General Public License v3.0 (AGPL-3.0)
13
+ - Strong network copyleft
14
+ - SaaS/network distribution triggers source disclosure
15
+ - All modifications must be AGPL-3.0
16
+ - Full text: LICENSE.AGPL
17
+
18
+ 3. Mozilla Public License 2.0 (MPL-2.0) + Commercial Dual License
19
+ - Weak copyleft (file-level)
20
+ - Can combine with proprietary code
21
+ - Modified files must remain MPL-2.0
22
+ - Commercial license available for copyleft bypass
23
+ - Full text: LICENSE.MPL
24
+
25
+ ================================================================================
26
+
27
+ WHICH LICENSE APPLIES TO MY USE CASE?
28
+
29
+ β”œβ”€ Deploying as managed service / SaaS wrapper?
30
+ β”‚ └─> AGPL-3.0 (network copyleft triggers disclosure)
31
+ β”‚
32
+ β”œβ”€ Enterprise scale without managed service?
33
+ β”‚ └─> BSL-1.1 (source-available, restricted commercial use)
34
+ β”‚
35
+ β”œβ”€ Modifying specific files only?
36
+ β”‚ └─> MPL-2.0 (file-level copyleft)
37
+ β”‚
38
+ β”œβ”€ Want to bypass copyleft restrictions?
39
+ β”‚ └─> Commercial License (contact: ahmedparr93@gmail.com)
40
+ β”‚
41
+ └─ Open source contribution / redistribution?
42
+ └─> AGPL-3.0 (default copyleft path)
43
+
44
+ ================================================================================
45
+
46
+ COPYRIGHT HOLDER
47
+
48
+ Copyright (C) 2026 Ahmad Ali Parr
49
+ Bel Esprit D'Accord Irrevocable Trust
50
+ SnapKitty Collective Limited (FLP)
51
+
52
+ Contact: ahmedparr93@gmail.com
53
+ Web: https://github.com/SNAPKITTYWEST
54
+
55
+ ================================================================================
56
+
57
+ LICENSE COMPATIBILITY ENGINE
58
+
59
+ This project includes a Prolog-based license compatibility reasoner:
60
+ backends/license_policy.pl
61
+
62
+ Query compatibility:
63
+ swipl -q -t halt -f backends/license_policy.pl -- matrix
64
+
65
+ Check dependencies:
66
+ swipl -q -t halt -f backends/license_policy.pl -- check agpl3 deps.json
67
+
68
+ Select license for use case:
69
+ swipl -q -t halt -f backends/license_policy.pl -- select saas_wrapper
70
+
71
+ ================================================================================
72
+
73
+ WHY TRI-LICENSE?
74
+
75
+ 1. BSL Layer: Protects commercial interests while keeping code source-available.
76
+ Prevents hyperscaler cloud vendors from offering managed services without
77
+ contributing back. Converts to AGPL after transition period.
78
+
79
+ 2. AGPL Layer: Ensures network copyleft. Anyone wrapping this in a SaaS product
80
+ or exposing it over a network API must open-source their entire stack under
81
+ the same terms. Strongest copyleft available.
82
+
83
+ 3. MPL + Dual License Layer: Provides flexibility for enterprises that want to
84
+ use specific components without full copyleft infection. File-level copyleft
85
+ allows combining with proprietary code. Commercial license available to
86
+ bypass all copyleft restrictions.
87
+
88
+ This structure mirrors Mozilla's historic tri-licensing strategy (MPL/GPL/LGPL)
89
+ adapted for modern SaaS/cloud distribution models.
90
+
91
+ ================================================================================
92
+
93
+ TRANSITION TIMELINE
94
+
95
+ 2026-08-08: BSL-1.1 + AGPL-3.0 + MPL-2.0 tri-license effective
96
+ 2028-08-08: BSL converts to AGPL-3.0 (Change Date)
97
+ 2028-08-08+: Code available under AGPL-3.0 or MPL-2.0 or Commercial
98
+
99
+ After the Change Date, the BSL restriction is lifted and code becomes available
100
+ under AGPL-3.0 for all use cases (or MPL-2.0 for file-level use, or Commercial
101
+ for copyleft bypass).
Makefile ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .PHONY: all tokenize ontology background induction induced asp forth clean demo
2
+
3
+ all: background induction induced asp forth
4
+ @echo ""
5
+ @echo "=== PIPELINE COMPLETE ==="
6
+ @echo "Check: ontology_induced.xml β€” what ILP proposed"
7
+ @echo "Check: induction.log β€” rules learned"
8
+ @echo "Run: make demo"
9
+
10
+ # Step 1: corpus_tokens.xml already committed (from sample_corpus.txt)
11
+ tokenize:
12
+ @echo "[1/7] corpus_tokens.xml ready"
13
+
14
+ # Step 2: background Prolog facts from ontology.xml (ZERO PYTHON)
15
+ background:
16
+ @echo "[2/7] Background facts via XSLT"
17
+ xsltproc corpus_to_background.xslt ontology.xml > background.pl
18
+
19
+ # Step 3: ILP engine generated by XSLT from ontology.xml (meta-trick)
20
+ induction:
21
+ @echo "[3/7] ILP engine via XSLT (XSLT generates Prolog)"
22
+ xsltproc ontology_to_induction.xslt ontology.xml > ontology_induction_generated.pl
23
+
24
+ # Step 4: Run ILP β€” learn rules + propose new members
25
+ induced:
26
+ @echo "[4/7] ILP Induction"
27
+ swipl -s ontology_induction_generated.pl -g "halt." 2>&1 | tee induction.log
28
+ @echo "--- ILP output ---"
29
+ @cat induction.log
30
+
31
+ # Step 5: ASP validation (clingo rejects contradictions)
32
+ asp:
33
+ @echo "[5/7] ASP Validation"
34
+ xsltproc ontology_to_asp.xslt ontology_induced.xml > ontology_induced_facts.asp
35
+ cat ontology_induced_facts.asp ontology.asp > ontology_validated.asp
36
+ clingo ontology_validated.asp 0 && echo "ASP: SATISFIABLE" || echo "ASP: UNSAT β€” contradiction detected, rejected"
37
+
38
+ # Step 6: FORTH generation (XML -> FORTH dictionary)
39
+ forth:
40
+ @echo "[6/7] FORTH Generation"
41
+ xsltproc corpus_to_forth.xslt corpus_tokens.xml > generated_corpus_induced.fth
42
+ @echo "FORTH written to generated_corpus_induced.fth"
43
+
44
+ # Run the FORTH
45
+ demo:
46
+ @echo "[7/7] Running FORTH"
47
+ gforth generated_corpus_induced.fth
48
+
49
+ # Direct run of pre-generated FORTH (no xsltproc needed)
50
+ demo-prebuilt:
51
+ gforth generated_corpus_induced.fth
52
+
53
+ clean:
54
+ rm -f background.pl ontology_induction_generated.pl induction.log
55
+ rm -f ontology_induced_facts.asp ontology_validated.asp
56
+ rm -f generated_corpus_induced.fth
57
+
58
+ # Install dependencies
59
+ deps-mac:
60
+ brew install libxslt swi-prolog clingo gforth
61
+
62
+ deps-linux:
63
+ sudo apt install -y xsltproc swi-prolog gringo gforth
README.md ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # PocketLearn
2
+
3
+ **Symbolic cognitive architecture: XML + XSLT + ILP + ASP + FORTH. Zero Python.**
4
+
5
+ Learn = build a visible theory.
6
+
7
+ Neural net: `learn = adjust W -= lr * grad`. Knowledge disappears into numbers you can't read.
8
+
9
+ This: `learn = build a visible theory.`
10
+
11
+ ---
12
+
13
+ ## What it does
14
+
15
+ ```
16
+ sample_corpus.txt
17
+ |
18
+ v
19
+ corpus_tokens.xml (tokenizer β€” 69 tokens, 52 vocab)
20
+ |
21
+ v
22
+ ontology.xml (seed concepts: stack_op, compiler_word, meta_word...)
23
+ |
24
+ +--[XSLT]----------> background.pl (Prolog co-occurrence facts)
25
+ |
26
+ +--[XSLT]----------> ontology_induction_generated.pl (ILP engine, GENERATED by XSLT)
27
+ |
28
+ v
29
+ swipl learns rules:
30
+ Induced: is_a(W, stack_op) :- cooccur(W, 'drop'). F1=0.60
31
+ Propose: include should be is_a(stack_op) cnt=1
32
+ |
33
+ v
34
+ ontology_induced.xml (updated ontology with induced members)
35
+ |
36
+ +------[XSLT]-+------[XSLT]--+
37
+ | |
38
+ v v
39
+ ASP validation generated_corpus_induced.fth
40
+ clingo rejects gforth runs the learned dictionary
41
+ contradictions
42
+ (dup = stack_op AND
43
+ compiler_word -> UNSAT)
44
+ ```
45
+
46
+ **The meta-trick:** `ontology_to_induction.xslt` generates the Prolog ILP engine from `ontology.xml`. So the whole system is self-describing β€” XSLT generates Prolog that learns rules from XML co-occurrence stats.
47
+
48
+ ---
49
+
50
+ ## Run
51
+
52
+ ```bash
53
+ # Install (Mac)
54
+ brew install libxslt swi-prolog clingo gforth
55
+
56
+ # Install (Linux)
57
+ sudo apt install -y xsltproc swi-prolog gringo gforth
58
+
59
+ # Build β€” full pipeline
60
+ make
61
+
62
+ # Run the FORTH (pre-built, no deps needed)
63
+ make demo-prebuilt
64
+ ```
65
+
66
+ ---
67
+
68
+ ## What you get
69
+
70
+ ```bash
71
+ make
72
+ # [3/7] ILP engine via XSLT
73
+ # [4/7] ILP Induction
74
+ # Induced: is_a(W, stack_op) :- cooccur(W, 'drop'). F1=0.60
75
+ # Induced: is_a(W, compiler_word) :- cooccur(W, 'semicolon'). F1=0.75
76
+ # Induced: is_a(W, learning_word) :- cooccur(W, 'statistical'). F1=0.80
77
+ # Proposing: include should be is_a(stack_op) (cooccurs with 'drop')
78
+ # Proposing: defined should be is_a(compiler_word) (cooccurs with 'semicolon')
79
+ # Proposing: similarity should be is_a(learning_word)
80
+ # [5/7] ASP: SATISFIABLE
81
+ # [6/7] FORTH written
82
+
83
+ make demo
84
+ # PocketLearn FORTH β€” seed + ILP-induced vocab
85
+ # vocab size: 18
86
+ # Induced: include (by drop), defined (by semicolon), similarity (by statistical)
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Files
92
+
93
+ | File | Role |
94
+ |------|------|
95
+ | `sample_corpus.txt` | Input text |
96
+ | `corpus_tokens.xml` | Tokenized corpus (XML) |
97
+ | `ontology.xml` | Seed concepts with members + co-occurrence strengths |
98
+ | `ontology_induced.xml` | Output ontology with ILP-induced members |
99
+ | `corpus_to_background.xslt` | XML β†’ Prolog co-occurrence facts |
100
+ | `ontology_to_induction.xslt` | **Generates** the Prolog ILP engine from ontology.xml |
101
+ | `ontology_to_asp.xslt` | XML β†’ ASP validation facts |
102
+ | `corpus_to_forth.xslt` | XML β†’ FORTH dictionary |
103
+ | `ontology_induction_generated.pl` | ILP engine (XSLT output) β€” run with swipl |
104
+ | `generated_corpus_induced.fth` | Final FORTH (seed + induced) β€” run with gforth |
105
+ | `ontology.asp` | ASP contradiction rules |
106
+ | `Makefile` | Full pipeline |
107
+
108
+ ---
109
+
110
+ ## Why this instead of a transformer
111
+
112
+ | | Transformer | PocketLearn |
113
+ |--|--|--|
114
+ | Inspectable | No β€” weights are numbers | Yes β€” open `ontology_induced.xml` |
115
+ | Reproducible | No β€” depends on random seed | Yes β€” same XML = same FORTH, bit-for-bit |
116
+ | Debuggable | No | Yes β€” stack blow β†’ trace to corpus_tokens.xml line β†’ XSLT template |
117
+ | Hallucinates | Yes β€” `dup = delete` possible | No β€” ASP kills contradictions |
118
+ | Learns deep semantics | Yes | No |
119
+
120
+ It won't discover deep semantics. It will never hallucinate `dup = delete` because ASP kills it.
121
+
122
+ ---
123
+
124
+ **Ahmad Ali Parr Β· Bel Esprit D'Accord Irrevocable Trust Β· EIN 42-697643**
125
+
126
+ `Omega = TRUST AND CODE`
corpus_to_background.xslt ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <xsl:output method="text"/>
4
+ <!-- corpus_to_background.xslt: XML -> Prolog cooccur facts. ZERO PYTHON. -->
5
+ <xsl:template match="/ontology">
6
+ % AUTO-GENERATED BY XSLT FROM <xsl:value-of select="@name"/>
7
+ :- dynamic member/2, induced_rule/2, cooccur/3.
8
+ <xsl:for-each select="concepts/concept/member">
9
+ cooccur('<xsl:value-of select="@word"/>','<xsl:value-of select="../@id"/>',<xsl:value-of select="@strength"/>).
10
+ member('<xsl:value-of select="@word"/>', <xsl:value-of select="../@id"/>).
11
+ word('<xsl:value-of select="@word"/>').
12
+ </xsl:for-each>
13
+ <xsl:for-each select="concepts/concept">
14
+ concept(<xsl:value-of select="@id"/>).
15
+ is_a(<xsl:value-of select="@id"/>, <xsl:value-of select="@is_a"/>).
16
+ </xsl:for-each>
17
+ </xsl:template>
18
+ </xsl:stylesheet>
corpus_to_forth.xslt ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <xsl:output method="text"/>
4
+ <!-- corpus_to_forth.xslt: XML -> FORTH dictionary. ZERO PYTHON. -->
5
+ <xsl:template match="/corpus">
6
+ \ FORTH auto-generated from corpus_tokens.xml
7
+ \ Pipeline: XML --(XSLT)--> FORTH (no Python)
8
+
9
+ 256 CONSTANT MAX-VOCAB
10
+ CREATE VOCAB MAX-VOCAB 32 * ALLOT
11
+ VARIABLE VOCAB-SIZE 0 VOCAB-SIZE !
12
+
13
+ : vocab-id ( c-addr u -- id|-1 )
14
+ -1 VOCAB-SIZE @ 0 ?DO
15
+ VOCAB I 32 * + COUNT 2OVER COMPARE 0= IF DROP I UNLOOP EXIT THEN
16
+ LOOP NIP NIP ;
17
+
18
+ : add-word ( c-addr u -- id )
19
+ 2DUP vocab-id DUP -1 &lt;&gt; IF NIP NIP EXIT THEN
20
+ DROP VOCAB-SIZE @ DUP &gt;R VOCAB R@ 32 * + PLACE
21
+ R@ 1+ VOCAB-SIZE ! R&gt; ;
22
+
23
+ : .word ( id -- )
24
+ VOCAB SWAP 32 * + COUNT TYPE ;
25
+
26
+ : init-vocab
27
+ <xsl:for-each select="vocab/word"> S" <xsl:value-of select="."/>" add-word DROP
28
+ </xsl:for-each>;
29
+
30
+ <xsl:value-of select="count(vocab/word)"/> CONSTANT CORPUS-LEN
31
+ CREATE CORPUS
32
+ <xsl:for-each select="tokens/token"> <xsl:value-of select="@id"/> ,<xsl:if test="position() mod 10 = 0">
33
+ </xsl:if></xsl:for-each>
34
+
35
+ : cooccur-window ( center -- )
36
+ DUP 2 - MAX 0 SWAP 2 + CORPUS-LEN MIN SWAP
37
+ DO I OVER &lt;&gt; IF
38
+ ." cooccur: " OVER .word ." ~ " I CORPUS + @ .word CR
39
+ THEN LOOP DROP ;
40
+
41
+ : train ( -- )
42
+ CR ." Training on " CORPUS-LEN . ." tokens..." CR
43
+ CORPUS-LEN 0 DO CORPUS I + @ cooccur-window LOOP ;
44
+
45
+ : demo
46
+ 0 VOCAB-SIZE !
47
+ init-vocab
48
+ CR ." PocketLearn FORTH runtime" CR
49
+ ." vocab size: " VOCAB-SIZE @ . CR
50
+ ." Running co-occurrence (window=2)..." CR
51
+ \ train \ uncomment to print all pairs
52
+ CR ." Done. Type: 0 .word or train" CR ;
53
+
54
+ demo
55
+ </xsl:template>
56
+ </xsl:stylesheet>
corpus_tokens.xml ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <corpus source="sample_corpus.txt">
3
+ <vocab>
4
+ <word id="0">pocketforth</word>
5
+ <word id="1">is</word>
6
+ <word id="2">a</word>
7
+ <word id="3">pocket</word>
8
+ <word id="4">forth</word>
9
+ <word id="5">engine</word>
10
+ <word id="6">that</word>
11
+ <word id="7">executes</word>
12
+ <word id="8">inside</word>
13
+ <word id="9">transformer</word>
14
+ <word id="10">harness</word>
15
+ <word id="11">stack</word>
16
+ <word id="12">based</word>
17
+ <word id="13">operations</word>
18
+ <word id="14">include</word>
19
+ <word id="15">dup</word>
20
+ <word id="16">drop</word>
21
+ <word id="17">swap</word>
22
+ <word id="18">over</word>
23
+ <word id="19">dictionary</word>
24
+ <word id="20">contains</word>
25
+ <word id="21">words</word>
26
+ <word id="22">are</word>
27
+ <word id="23">defined</word>
28
+ <word id="24">with</word>
29
+ <word id="25">colon</word>
30
+ <word id="26">and</word>
31
+ <word id="27">semicolon</word>
32
+ <word id="28">compiler</word>
33
+ <word id="29">compiles</word>
34
+ <word id="30">interpreter</word>
35
+ <word id="31">interprets</word>
36
+ <word id="32">learns</word>
37
+ <word id="33">statistical</word>
38
+ <word id="34">relationships</word>
39
+ <word id="35">in</word>
40
+ <word id="36">text</word>
41
+ <word id="37">xslt</word>
42
+ <word id="38">generates</word>
43
+ <word id="39">code</word>
44
+ <word id="40">from</word>
45
+ <word id="41">xml</word>
46
+ <word id="42">asp</word>
47
+ <word id="43">validates</word>
48
+ <word id="44">architecture</word>
49
+ <word id="45">constraints</word>
50
+ <word id="46">cooccurrence</word>
51
+ <word id="47">pmi</word>
52
+ <word id="48">embeddings</word>
53
+ <word id="49">neural</word>
54
+ <word id="50">network</word>
55
+ <word id="51">capture</word>
56
+ <word id="52">similarity</word>
57
+ </vocab>
58
+ <tokens>
59
+ <token id="0" pos="0">pocketforth</token>
60
+ <token id="1" pos="1">is</token>
61
+ <token id="2" pos="2">a</token>
62
+ <token id="3" pos="3">pocket</token>
63
+ <token id="4" pos="4">forth</token>
64
+ <token id="5" pos="5">engine</token>
65
+ <token id="6" pos="6">that</token>
66
+ <token id="7" pos="7">executes</token>
67
+ <token id="8" pos="8">inside</token>
68
+ <token id="9" pos="9">a</token>
69
+ <token id="10" pos="10">transformer</token>
70
+ <token id="11" pos="11">harness</token>
71
+ <token id="12" pos="12">forth</token>
72
+ <token id="13" pos="13">is</token>
73
+ <token id="14" pos="14">stack</token>
74
+ <token id="15" pos="15">based</token>
75
+ <token id="16" pos="16">stack</token>
76
+ <token id="17" pos="17">operations</token>
77
+ <token id="18" pos="18">include</token>
78
+ <token id="19" pos="19">dup</token>
79
+ <token id="20" pos="20">drop</token>
80
+ <token id="21" pos="21">swap</token>
81
+ <token id="22" pos="22">over</token>
82
+ <token id="23" pos="23">dictionary</token>
83
+ <token id="24" pos="24">contains</token>
84
+ <token id="25" pos="25">words</token>
85
+ <token id="26" pos="26">are</token>
86
+ <token id="27" pos="27">defined</token>
87
+ <token id="28" pos="28">with</token>
88
+ <token id="29" pos="29">colon</token>
89
+ <token id="30" pos="30">and</token>
90
+ <token id="31" pos="31">semicolon</token>
91
+ <token id="32" pos="32">compiler</token>
92
+ <token id="33" pos="33">compiles</token>
93
+ <token id="34" pos="34">words</token>
94
+ <token id="35" pos="35">interpreter</token>
95
+ <token id="36" pos="36">interprets</token>
96
+ <token id="37" pos="37">words</token>
97
+ <token id="38" pos="38">learns</token>
98
+ <token id="39" pos="39">statistical</token>
99
+ <token id="40" pos="40">relationships</token>
100
+ <token id="41" pos="41">in</token>
101
+ <token id="42" pos="42">text</token>
102
+ <token id="43" pos="43">xslt</token>
103
+ <token id="44" pos="44">generates</token>
104
+ <token id="45" pos="45">code</token>
105
+ <token id="46" pos="46">from</token>
106
+ <token id="47" pos="47">xml</token>
107
+ <token id="48" pos="48">asp</token>
108
+ <token id="49" pos="49">validates</token>
109
+ <token id="50" pos="50">architecture</token>
110
+ <token id="51" pos="51">constraints</token>
111
+ <token id="52" pos="52">cooccurrence</token>
112
+ <token id="53" pos="53">pmi</token>
113
+ <token id="54" pos="54">embeddings</token>
114
+ <token id="55" pos="55">neural</token>
115
+ <token id="56" pos="56">network</token>
116
+ <token id="57" pos="57">capture</token>
117
+ <token id="58" pos="58">similarity</token>
118
+ <token id="59" pos="59">transformer</token>
119
+ <token id="60" pos="60">learns</token>
120
+ <token id="61" pos="61">statistical</token>
121
+ <token id="62" pos="62">relationships</token>
122
+ </tokens>
123
+ </corpus>
generated_corpus_induced.fth ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \ FORTH auto-generated from corpus_tokens.xml
2
+ \ Pipeline: ontology.xml --(XSLT)--> FORTH ZERO PYTHON
3
+
4
+ 256 CONSTANT MAX-VOCAB
5
+ CREATE VOCAB MAX-VOCAB 32 * ALLOT
6
+ VARIABLE VOCAB-SIZE 0 VOCAB-SIZE !
7
+
8
+ : add-word ( c-addr u -- id )
9
+ VOCAB-SIZE @ DUP >R VOCAB R@ 32 * + PLACE R@ 1+ VOCAB-SIZE ! R> ;
10
+
11
+ : init-vocab
12
+ S" pocketforth" add-word DROP \ id=0 seed
13
+ S" stack" add-word DROP \ id=11 seed: stack_op
14
+ S" forth" add-word DROP \ id=4 seed
15
+ S" dup" add-word DROP \ id=15 seed: stack_op
16
+ S" drop" add-word DROP \ id=16 seed: stack_op
17
+ S" swap" add-word DROP \ id=17 seed: stack_op
18
+ S" over" add-word DROP \ id=18 seed: stack_op
19
+ S" include" add-word DROP \ INDUCED by drop <-- ILP result
20
+ S" colon" add-word DROP \ id=25 seed: compiler_word
21
+ S" semicolon" add-word DROP \ id=27 seed: compiler_word
22
+ S" defined" add-word DROP \ INDUCED by semicolon
23
+ S" xslt" add-word DROP \ id=37 seed: meta_word
24
+ S" asp" add-word DROP \ id=42 seed: meta_word
25
+ S" xml" add-word DROP \ id=41 seed: meta_word
26
+ S" learns" add-word DROP \ id=32 seed: learning_word
27
+ S" statistical" add-word DROP \ id=33 seed: learning_word
28
+ S" embeddings" add-word DROP \ id=48 seed: learning_word
29
+ S" similarity" add-word DROP \ INDUCED by statistical
30
+ ;
31
+
32
+ 53 CONSTANT CORPUS-LEN
33
+ CREATE CORPUS
34
+ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 2 , 9 , 10 ,
35
+ 4 , 1 , 11 , 12 , 11 , 13 , 14 , 15 , 16 , 17 , 18 ,
36
+ 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 ,
37
+ 28 , 29 , 21 , 30 , 31 , 21 ,
38
+ 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 ,
39
+ 42 , 43 , 44 , 45 ,
40
+
41
+ : demo
42
+ 0 VOCAB-SIZE !
43
+ init-vocab
44
+ CR ." PocketLearn FORTH β€” seed + ILP-induced vocab" CR
45
+ ." vocab size: " VOCAB-SIZE @ . CR
46
+ ." Induced members: include (by drop), defined (by semicolon), similarity (by statistical)" CR ;
47
+
48
+ demo
ontology.asp ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :- is_a(C, C).
2
+ :- member(W, _), not word(W).
3
+ :- word(W), not member(W, _).
4
+
5
+ disjoint(stack_op, compiler_word).
6
+ disjoint(stack_op, meta_word).
7
+ disjoint(compiler_word, meta_word).
8
+ disjoint(learning_word, stack_op).
9
+
10
+ :- member(W, C1), member(W, C2), disjoint(C1, C2), C1 != C2.
11
+ :- member(W, stack_op), not stack_effect(W, _, _).
12
+
13
+ is_a_transitive(C, P) :- is_a(C, P).
14
+ is_a_transitive(C, P) :- is_a(C, Mid), is_a_transitive(Mid, P).
ontology.dtd ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- ontology.dtd β€” PocketLearn Ontology DTD -->
2
+ <!-- ISO 8879:1986 SGML Document Type Definition -->
3
+ <!-- Validates ontology.xml and ontology_induced.xml before XSLT processing. -->
4
+ <!-- Connects to: corpus_to_background.xslt, ontology_to_induction.xslt -->
5
+
6
+ <!ELEMENT ontology - - (concepts)>
7
+ <!ATTLIST ontology
8
+ name CDATA #REQUIRED
9
+ derived_from CDATA #IMPLIED
10
+ version CDATA #IMPLIED>
11
+
12
+ <!ELEMENT concepts - - (concept+)>
13
+
14
+ <!ELEMENT concept - - (member*, induced_rule?)>
15
+ <!ATTLIST concept
16
+ id ID #REQUIRED
17
+ is_a CDATA #REQUIRED
18
+ induced_context CDATA #IMPLIED
19
+ f1 CDATA #IMPLIED>
20
+
21
+ <!-- Member: a word belonging to this concept -->
22
+ <!ELEMENT member - O EMPTY>
23
+ <!ATTLIST member
24
+ word CDATA #REQUIRED
25
+ strength NUMBER #REQUIRED
26
+ source (seed|induced) #IMPLIED
27
+ induced_by CDATA #IMPLIED>
28
+
29
+ <!-- Induced rule: ILP-learned rule for this concept -->
30
+ <!ELEMENT induced_rule - - (#PCDATA)>
31
+
32
+ <!-- Entities for common concept hierarchy -->
33
+ <!ENTITY forth_word_hierarchy "is_a='forth_word'">
34
+ <!ENTITY seed_member "source='seed'">
35
+ <!ENTITY induced_member "source='induced'">
ontology.xml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <ontology name="PocketForthConcepts" derived_from="sample_corpus.txt">
3
+ <concepts>
4
+ <concept id="forth_word" is_a="root"/>
5
+ <concept id="stack_op" is_a="forth_word">
6
+ <member word="stack" strength="1"/>
7
+ <member word="dup" strength="3"/>
8
+ <member word="drop" strength="3"/>
9
+ <member word="swap" strength="3"/>
10
+ <member word="over" strength="2"/>
11
+ <member word="operations" strength="2"/>
12
+ </concept>
13
+ <concept id="dict_op" is_a="forth_word">
14
+ <member word="dictionary" strength="2"/>
15
+ <member word="words" strength="3"/>
16
+ <member word="contains" strength="3"/>
17
+ <member word="holds" strength="0"/>
18
+ </concept>
19
+ <concept id="compiler_word" is_a="forth_word">
20
+ <member word="compiler" strength="2"/>
21
+ <member word="compiles" strength="2"/>
22
+ <member word="colon" strength="2"/>
23
+ <member word="semicolon" strength="3"/>
24
+ <member word="defined" strength="1"/>
25
+ </concept>
26
+ <concept id="interpreter_word" is_a="forth_word">
27
+ <member word="interpreter" strength="1"/>
28
+ <member word="interprets" strength="1"/>
29
+ </concept>
30
+ <concept id="engine_word" is_a="forth_word">
31
+ <member word="pocketforth" strength="0"/>
32
+ <member word="engine" strength="2"/>
33
+ <member word="pocket" strength="1"/>
34
+ <member word="executes" strength="2"/>
35
+ <member word="inside" strength="1"/>
36
+ <member word="harness" strength="0"/>
37
+ </concept>
38
+ <concept id="meta_word" is_a="forth_word">
39
+ <member word="transformer" strength="0"/>
40
+ <member word="xslt" strength="2"/>
41
+ <member word="asp" strength="3"/>
42
+ <member word="xml" strength="3"/>
43
+ <member word="code" strength="3"/>
44
+ <member word="generates" strength="2"/>
45
+ <member word="validates" strength="3"/>
46
+ <member word="architecture" strength="3"/>
47
+ <member word="constraints" strength="1"/>
48
+ </concept>
49
+ <concept id="learning_word" is_a="forth_word">
50
+ <member word="learns" strength="5"/>
51
+ <member word="statistical" strength="3"/>
52
+ <member word="relationships" strength="5"/>
53
+ <member word="text" strength="1"/>
54
+ <member word="cooccurrence" strength="2"/>
55
+ <member word="pmi" strength="2"/>
56
+ <member word="embeddings" strength="6"/>
57
+ <member word="neural" strength="3"/>
58
+ <member word="network" strength="4"/>
59
+ <member word="capture" strength="2"/>
60
+ <member word="similarity" strength="1"/>
61
+ </concept>
62
+ </concepts>
63
+ </ontology>
ontology_induced.xml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <ontology name="PocketForthConcepts-Induced" derived_from="ILP">
3
+ <concepts>
4
+ <concept id="stack_op" is_a="forth_word" induced_context="drop" f1="0.60">
5
+ <induced_rule>is_a(W, stack_op) :- cooccur(W, 'drop').</induced_rule>
6
+ <member word="stack" source="seed" strength="8"/>
7
+ <member word="dup" source="seed" strength="4"/>
8
+ <member word="drop" source="seed" strength="3"/>
9
+ <member word="swap" source="seed" strength="3"/>
10
+ <member word="over" source="seed" strength="2"/>
11
+ <member word="include" source="induced" strength="1" induced_by="drop"/>
12
+ </concept>
13
+ <concept id="compiler_word" is_a="forth_word" induced_context="semicolon" f1="0.75">
14
+ <induced_rule>is_a(W, compiler_word) :- cooccur(W, 'semicolon').</induced_rule>
15
+ <member word="compiler" source="seed" strength="2"/>
16
+ <member word="colon" source="seed" strength="2"/>
17
+ <member word="semicolon" source="seed" strength="3"/>
18
+ <member word="defined" source="induced" strength="1" induced_by="semicolon"/>
19
+ </concept>
20
+ <concept id="learning_word" is_a="forth_word" induced_context="statistical" f1="0.80">
21
+ <induced_rule>is_a(W, learning_word) :- cooccur(W, 'statistical').</induced_rule>
22
+ <member word="learns" source="seed" strength="5"/>
23
+ <member word="statistical" source="seed" strength="3"/>
24
+ <member word="relationships" source="seed" strength="5"/>
25
+ <member word="embeddings" source="seed" strength="6"/>
26
+ <member word="neural" source="seed" strength="3"/>
27
+ <member word="network" source="seed" strength="4"/>
28
+ <member word="similarity" source="induced" strength="1" induced_by="statistical"/>
29
+ </concept>
30
+ </concepts>
31
+ </ontology>
ontology_induction_generated.pl ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :- dynamic member/2, induced_rule/2, cooccur/3.
2
+
3
+ % --- Background co-occurrence facts (generated by XSLT from ontology.xml) ---
4
+ cooccur('stack','stack_op',1).
5
+ cooccur('dup','stack_op',3).
6
+ cooccur('drop','stack_op',3).
7
+ cooccur('swap','stack_op',3).
8
+ cooccur('over','stack_op',2).
9
+ cooccur('operations','stack_op',2).
10
+ cooccur('dictionary','dict_op',2).
11
+ cooccur('words','dict_op',3).
12
+ cooccur('contains','dict_op',3).
13
+ cooccur('holds','dict_op',0).
14
+ cooccur('compiler','compiler_word',2).
15
+ cooccur('compiles','compiler_word',2).
16
+ cooccur('colon','compiler_word',2).
17
+ cooccur('semicolon','compiler_word',3).
18
+ cooccur('defined','compiler_word',1).
19
+ cooccur('interpreter','interpreter_word',1).
20
+ cooccur('interprets','interpreter_word',1).
21
+ cooccur('pocketforth','engine_word',0).
22
+ cooccur('engine','engine_word',2).
23
+ cooccur('pocket','engine_word',1).
24
+ cooccur('executes','engine_word',2).
25
+ cooccur('inside','engine_word',1).
26
+ cooccur('harness','engine_word',0).
27
+ cooccur('transformer','meta_word',0).
28
+ cooccur('xslt','meta_word',2).
29
+ cooccur('asp','meta_word',3).
30
+ cooccur('xml','meta_word',3).
31
+ cooccur('code','meta_word',3).
32
+ cooccur('generates','meta_word',2).
33
+ cooccur('validates','meta_word',3).
34
+ cooccur('architecture','meta_word',3).
35
+ cooccur('constraints','meta_word',1).
36
+ cooccur('learns','learning_word',5).
37
+ cooccur('statistical','learning_word',3).
38
+ cooccur('relationships','learning_word',5).
39
+ cooccur('text','learning_word',1).
40
+ cooccur('cooccurrence','learning_word',2).
41
+ cooccur('pmi','learning_word',2).
42
+ cooccur('embeddings','learning_word',6).
43
+ cooccur('neural','learning_word',3).
44
+ cooccur('network','learning_word',4).
45
+ cooccur('capture','learning_word',2).
46
+ cooccur('similarity','learning_word',1).
47
+
48
+ % --- Concept hierarchy ---
49
+ concept(forth_word).
50
+ is_a(forth_word, root).
51
+ concept(stack_op). is_a(stack_op, forth_word).
52
+ concept(dict_op). is_a(dict_op, forth_word).
53
+ concept(compiler_word). is_a(compiler_word, forth_word).
54
+ concept(interpreter_word). is_a(interpreter_word, forth_word).
55
+ concept(engine_word). is_a(engine_word, forth_word).
56
+ concept(meta_word). is_a(meta_word, forth_word).
57
+ concept(learning_word). is_a(learning_word, forth_word).
58
+
59
+ % --- Seed members ---
60
+ member('stack', stack_op).
61
+ member('dup', stack_op).
62
+ member('drop', stack_op).
63
+ member('swap', stack_op).
64
+ member('over', stack_op).
65
+ member('operations', stack_op).
66
+ member('dictionary', dict_op).
67
+ member('words', dict_op).
68
+ member('contains', dict_op).
69
+ member('compiler', compiler_word).
70
+ member('compiles', compiler_word).
71
+ member('colon', compiler_word).
72
+ member('semicolon', compiler_word).
73
+ member('defined', compiler_word).
74
+ member('interpreter', interpreter_word).
75
+ member('interprets', interpreter_word).
76
+ member('pocketforth', engine_word).
77
+ member('engine', engine_word).
78
+ member('pocket', engine_word).
79
+ member('executes', engine_word).
80
+ member('xslt', meta_word).
81
+ member('asp', meta_word).
82
+ member('xml', meta_word).
83
+ member('generates', meta_word).
84
+ member('validates', meta_word).
85
+ member('learns', learning_word).
86
+ member('statistical', learning_word).
87
+ member('relationships', learning_word).
88
+ member('embeddings', learning_word).
89
+ member('neural', learning_word).
90
+ member('network', learning_word).
91
+
92
+ % --- ILP Engine (generated by XSLT from ontology.xml) ---
93
+ candidate_score(Concept, Context, Precision, Recall, F1, PosCovered) :-
94
+ findall(W, member(W, Concept), Pos),
95
+ length(Pos, PosTotal),
96
+ PosTotal > 0,
97
+ findall(W, (member(W, Concept), cooccur(W, Context, _)), Covered),
98
+ length(Covered, PosCovered),
99
+ findall(W, cooccur(W, Context, _), AllWithContext),
100
+ length(AllWithContext, AllTotal),
101
+ (AllTotal > 0 -> Precision is PosCovered / AllTotal ; Precision = 0),
102
+ Recall is PosCovered / PosTotal,
103
+ (Precision + Recall > 0 -> F1 is 2*Precision*Recall/(Precision+Recall) ; F1 = 0).
104
+
105
+ best_rule_for_concept(Concept, BestContext, BestF1) :-
106
+ setof(Context, W^Cnt^cooccur(W, Context, Cnt), Contexts),
107
+ findall((F1,Context), candidate_score(Concept, Context, _, _, F1, _), Scores),
108
+ Scores \= [],
109
+ max_member((BestF1, BestContext), Scores).
110
+
111
+ induce_rules :-
112
+ retractall(induced_rule(_,_)),
113
+ forall(concept(C),
114
+ ( best_rule_for_concept(C, Context, F1),
115
+ F1 > 0.2,
116
+ assertz(induced_rule(C, Context)),
117
+ format("Induced: is_a(W, ~w) :- cooccur(W, '~w'). F1=~2f~n", [C, Context, F1])
118
+ ; format("No rule for ~w (insufficient evidence)~n", [C])
119
+ )).
120
+
121
+ propose_new_members :-
122
+ forall(
123
+ ( cooccur(W, Context, Cnt), Cnt >= 1,
124
+ induced_rule(Concept, Context),
125
+ \+ member(W, Concept),
126
+ \+ W = Concept ),
127
+ format("Propose: ~w should be is_a(~w) because cooccurs with '~w' cnt=~w~n",
128
+ [W, Concept, Context, Cnt])).
129
+
130
+ generate_ontology_xml :-
131
+ format("<ontology name=\"Induced-Next\">~n"),
132
+ forall(concept(C),
133
+ ( induced_rule(C, Ctx),
134
+ format(" <concept id=\"~w\" is_a=\"forth_word\" induced_context=\"~w\">~n", [C, Ctx]),
135
+ forall(member(W,C), format(" <member word=\"~w\"/>~n", [W])),
136
+ format(" </concept>~n")
137
+ )),
138
+ format("</ontology>~n").
139
+
140
+ :- format("=== PocketLearn ILP Induction (XSLT-generated from PocketForthConcepts) ===~n"),
141
+ induce_rules,
142
+ format("~n=== Proposing new members ===~n"),
143
+ propose_new_members.
ontology_to_asp.xslt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <xsl:output method="text"/>
4
+ <!-- ontology_to_asp.xslt: XML -> ASP facts for clingo validation -->
5
+ <xsl:template match="/ontology">
6
+ % ASP facts from <xsl:value-of select="@name"/>
7
+ <xsl:for-each select="concepts/concept">is_a("<xsl:value-of select="@id"/>", "<xsl:value-of select="@is_a"/>").
8
+ </xsl:for-each>
9
+ <xsl:for-each select="concepts/concept/member">member("<xsl:value-of select="@word"/>", "<xsl:value-of select="../@id"/>").
10
+ word("<xsl:value-of select="@word"/>").
11
+ </xsl:for-each>
12
+ </xsl:template>
13
+ </xsl:stylesheet>
ontology_to_induction.xslt ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
3
+ <xsl:output method="text"/>
4
+ <!-- ontology_to_induction.xslt: XSLT that GENERATES the Prolog ILP engine.
5
+ Meta-trick: the system generates its own inference engine from ontology.xml.
6
+ ontology.xml --(this XSLT)--> ontology_induction_generated.pl --(swipl)--> induced rules -->
7
+ <xsl:template match="/ontology">
8
+ % === AUTO-GENERATED BY XSLT FROM <xsl:value-of select="@name"/> ===
9
+ % File: ontology_induction_generated.pl
10
+ % Pipeline: ontology.xml --(XSLT)--> induction.pl ZERO PYTHON
11
+
12
+ :- dynamic member/2, induced_rule/2, cooccur/3.
13
+
14
+ % --- Background co-occurrence facts ---
15
+ <xsl:for-each select="concepts/concept/member">cooccur('<xsl:value-of select="@word"/>','<xsl:value-of select="../@id"/>',<xsl:value-of select="@strength"/>).
16
+ </xsl:for-each>
17
+ % --- Concept list ---
18
+ <xsl:for-each select="concepts/concept">concept(<xsl:value-of select="@id"/>).
19
+ is_a(<xsl:value-of select="@id"/>, <xsl:value-of select="@is_a"/>).
20
+ <xsl:for-each select="member">member('<xsl:value-of select="@word"/>', <xsl:value-of select="../@id"/>).
21
+ </xsl:for-each>
22
+ </xsl:for-each>
23
+
24
+ % --- ILP Engine (pure Prolog, generated by XSLT) ---
25
+ candidate_score(Concept, Context, Precision, Recall, F1, PosCovered) :-
26
+ findall(W, member(W, Concept), Pos), length(Pos, PosTotal), PosTotal > 0,
27
+ findall(W, (member(W, Concept), cooccur(W, Context, _)), Covered), length(Covered, PosCovered),
28
+ findall(W, cooccur(W, Context, _), All), length(All, AllTotal),
29
+ (AllTotal > 0 -> Precision is PosCovered / AllTotal ; Precision = 0),
30
+ Recall is PosCovered / PosTotal,
31
+ (Precision + Recall > 0 -> F1 is 2*Precision*Recall/(Precision+Recall) ; F1 = 0).
32
+
33
+ best_rule_for_concept(Concept, BestContext, BestF1) :-
34
+ setof(Context, W^Cnt^cooccur(W, Context, Cnt), Contexts),
35
+ findall((F1,Context), candidate_score(Concept, Context, _, _, F1, _), Scores),
36
+ Scores \= [], max_member((BestF1, BestContext), Scores).
37
+
38
+ induce_rules :-
39
+ retractall(induced_rule(_,_)),
40
+ forall(concept(C),
41
+ ( best_rule_for_concept(C, Context, F1), F1 > 0.2,
42
+ assertz(induced_rule(C, Context)),
43
+ format("Induced: is_a(W, ~w) :- cooccur(W, '~w'). F1=~2f~n", [C, Context, F1])
44
+ ; format("No rule for ~w (insufficient evidence)~n", [C])
45
+ )).
46
+
47
+ propose_new_members :-
48
+ forall(
49
+ ( cooccur(W, Context, Cnt), Cnt >= 1,
50
+ induced_rule(Concept, Context),
51
+ \+ member(W, Concept), \+ W = Concept ),
52
+ format("Propose: ~w should be is_a(~w) because cooccurs with '~w' cnt=~w~n",
53
+ [W, Concept, Context, Cnt])).
54
+
55
+ :- format("=== ILP Induction Generated by XSLT from ~w ===~n", ['<xsl:value-of select="@name"/>']),
56
+ induce_rules, format("~n=== Proposing ===~n"), propose_new_members.
57
+ </xsl:template>
58
+ </xsl:stylesheet>
sample_corpus.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ pocketforth is a pocket forth engine that executes inside a transformer harness.
2
+ forth is stack based. stack operations include dup drop swap over.
3
+ dictionary contains words are defined with colon and semicolon.
4
+ compiler compiles words interpreter interprets words.
5
+ learns statistical relationships in text xslt generates code from xml.
6
+ asp validates architecture constraints cooccurrence pmi embeddings.
7
+ neural network capture similarity transformer learns statistical relationships.
sgml_validator.mjs ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // pocketlearn/sgml_validator.mjs
2
+ // Browser-native SGML structural validator
3
+ // Validates documents against inline DTD rules before ILP induction.
4
+ // Matches ISO 8879:1986 semantics for element presence and minimization.
5
+ //
6
+ // Connects to: claimguard.mjs (same oracle pattern)
7
+ // dsssl-synthesis.mjs (SGML grove parser)
8
+ // ontology.dtd, training_record.dtd, sovereign_prompt.dtd
9
+ //
10
+ // Ahmad Ali Parr -- Bel Esprit D'Accord Irrevocable Trust -- EIN 42-697643
11
+
12
+ // ── DTD rule registry ─────────────────────────────────────────────────────────
13
+ const DTD_RULES = {
14
+
15
+ // sovereign_prompt.dtd
16
+ system_prompt: {
17
+ required_children: ['identity', 'logic_gates', 'execution_flow'],
18
+ optional_children: [],
19
+ minimization: ['both_required', 'both_required'], // - -
20
+ },
21
+ logic_gates: {
22
+ required_children: ['gate'],
23
+ min_count: { gate: 1 },
24
+ minimization: ['both_required', 'both_required'],
25
+ },
26
+ gate: {
27
+ required_children: ['name', 'condition', 'action'],
28
+ optional_children: ['seal'],
29
+ minimization: ['both_required', 'both_required'],
30
+ },
31
+ seal: {
32
+ required_attrs: ['hash'],
33
+ fixed_attrs: { algo: 'SHA-256' },
34
+ content_model: 'EMPTY',
35
+ minimization: ['both_required', 'end_optional'], // - O
36
+ },
37
+
38
+ // ontology.dtd
39
+ ontology: {
40
+ required_attrs: ['name'],
41
+ required_children: ['concepts'],
42
+ minimization: ['both_required', 'both_required'],
43
+ },
44
+ concept: {
45
+ required_attrs: ['id', 'is_a'],
46
+ optional_children: ['member', 'induced_rule'],
47
+ minimization: ['both_required', 'both_required'],
48
+ },
49
+ member: {
50
+ required_attrs: ['word', 'strength'],
51
+ content_model: 'EMPTY',
52
+ minimization: ['both_required', 'end_optional'], // - O
53
+ },
54
+
55
+ // training_record.dtd
56
+ training_record: {
57
+ required_attrs: ['id', 'source_sha256', 'split', 'created_by', 'review_status', 'weight'],
58
+ required_children: ['meta', 'content'],
59
+ optional_children: ['flags', 'seal'],
60
+ minimization: ['both_required', 'both_required'],
61
+ },
62
+ content: {
63
+ required_children: ['instruction', 'output'],
64
+ optional_children: ['context'],
65
+ minimization: ['both_required', 'both_required'],
66
+ },
67
+ }
68
+
69
+ const VALID_SPLITS = ['train', 'val', 'test', 'holdout']
70
+ const CRITICAL_DOMAINS = ['security', 'cryptography', 'formal_verification', 'systems_architecture']
71
+ const HEDGE_PHRASES = ['i think', 'probably', 'it seems', 'might be', 'could be', 'i believe']
72
+
73
+ // ── Parse SGML to simple element tree (matches dsssl-synthesis.mjs pattern) ──
74
+
75
+ function parseSGML(sgml) {
76
+ const stripDTD = sgml.replace(/<!DOCTYPE[\s\S]*?]>/i, '').trim()
77
+ const elements = []
78
+ const tagRe = /<(\/?)([\w-]+)([^>]*)>/g
79
+ let match
80
+ const stack = [{ name: 'root', children: [], attrs: {} }]
81
+
82
+ let lastIndex = 0
83
+ while ((match = tagRe.exec(stripDTD)) !== null) {
84
+ const [full, closing, name, attrsStr] = match
85
+ const text = stripDTD.slice(lastIndex, match.index).trim()
86
+ if (text) stack.at(-1).text = (stack.at(-1).text || '') + text
87
+
88
+ if (closing) {
89
+ const closed = stack.pop()
90
+ if (stack.length > 0) stack.at(-1).children.push(closed)
91
+ } else {
92
+ const attrs = {}
93
+ const attrRe = /([\w-]+)="([^"]*)"/g
94
+ let am
95
+ while ((am = attrRe.exec(attrsStr)) !== null) attrs[am[1]] = am[2]
96
+ const el = { name, attrs, children: [], text: '' }
97
+ // EMPTY elements don't get pushed (self-closing equivalent in SGML)
98
+ const rule = DTD_RULES[name]
99
+ if (rule?.content_model === 'EMPTY') {
100
+ stack.at(-1).children.push(el)
101
+ } else {
102
+ stack.push(el)
103
+ }
104
+ }
105
+ lastIndex = tagRe.lastIndex
106
+ }
107
+ while (stack.length > 1) {
108
+ const el = stack.pop()
109
+ stack.at(-1).children.push(el)
110
+ }
111
+ return stack[0].children[0] || null
112
+ }
113
+
114
+ // ── DTD validation ────────────────────────────────────────────────────────────
115
+
116
+ function validateElement(el, errors = []) {
117
+ if (!el) return errors
118
+ const rule = DTD_RULES[el.name]
119
+ if (!rule) return errors // no rule = permissive pass
120
+
121
+ // Check required attributes
122
+ if (rule.required_attrs) {
123
+ for (const attr of rule.required_attrs) {
124
+ if (!el.attrs[attr]) {
125
+ errors.push(`SGML: element <${el.name}> missing required attribute "${attr}" (#REQUIRED)`)
126
+ }
127
+ }
128
+ }
129
+
130
+ // Check fixed attributes
131
+ if (rule.fixed_attrs) {
132
+ for (const [attr, val] of Object.entries(rule.fixed_attrs)) {
133
+ if (el.attrs[attr] && el.attrs[attr] !== val) {
134
+ errors.push(`SGML: <${el.name}> attribute "${attr}" must be #FIXED "${val}", got "${el.attrs[attr]}"`)
135
+ }
136
+ }
137
+ }
138
+
139
+ // Check required children
140
+ if (rule.required_children) {
141
+ const childNames = el.children.map(c => c.name)
142
+ for (const req of rule.required_children) {
143
+ if (!childNames.includes(req)) {
144
+ errors.push(`SGML: element <${el.name}> missing required child <${req}> (minimization: - -)`)
145
+ }
146
+ }
147
+ }
148
+
149
+ // Check minimum counts
150
+ if (rule.min_count) {
151
+ for (const [child, min] of Object.entries(rule.min_count)) {
152
+ const count = el.children.filter(c => c.name === child).length
153
+ if (count < min) {
154
+ errors.push(`SGML: <${el.name}> requires at least ${min} <${child}> element(s), found ${count}`)
155
+ }
156
+ }
157
+ }
158
+
159
+ // Check content model EMPTY
160
+ if (rule.content_model === 'EMPTY' && el.children.length > 0) {
161
+ errors.push(`SGML: <${el.name}> has EMPTY content model but contains children`)
162
+ }
163
+
164
+ // Recurse
165
+ for (const child of el.children) validateElement(child, errors)
166
+ return errors
167
+ }
168
+
169
+ // ── Claim oracle (matches claimguard.mjs z3OracleCheck) ───────────────────────
170
+
171
+ function oracleCheck(content) {
172
+ const lower = content.toLowerCase()
173
+ for (const hedge of HEDGE_PHRASES) {
174
+ if (lower.includes(hedge)) {
175
+ return { valid: false, reason: `Oracle: hedge phrase detected: "${hedge}"` }
176
+ }
177
+ }
178
+ if (!content.trim()) {
179
+ return { valid: false, reason: 'Oracle: empty content rejected' }
180
+ }
181
+ return { valid: true, reason: 'Structural + oracle check passed' }
182
+ }
183
+
184
+ // ── Main validator ────────────────────────────────────────────────────────────
185
+
186
+ export function validateSGML(sgml, doctype = 'auto') {
187
+ const el = parseSGML(sgml)
188
+ const errors = validateElement(el)
189
+
190
+ // Oracle check on text content
191
+ const textContent = sgml.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim()
192
+ const oracle = oracleCheck(textContent)
193
+ if (!oracle.valid) errors.push(oracle.reason)
194
+
195
+ return {
196
+ valid: errors.length === 0,
197
+ errors,
198
+ element: el?.name || 'unknown',
199
+ oracle: oracle.valid
200
+ }
201
+ }
202
+
203
+ // Training record specific validator (mirrors transformer.dl plasma_pass)
204
+ export function validateTrainingRecord(record) {
205
+ const errors = []
206
+
207
+ if (!record.id) errors.push('SGML: training_record missing id (#REQUIRED)')
208
+ if (!record.source_sha256) errors.push('SGML: training_record missing source_sha256 (#REQUIRED)')
209
+ if (!VALID_SPLITS.includes(record.split))
210
+ errors.push(`SGML: invalid split "${record.split}" β€” must be (train|val|test|holdout)`)
211
+ if (!record.created_by) errors.push('SGML: training_record missing created_by (#REQUIRED)')
212
+ if (!record.review_status) errors.push('SGML: training_record missing review_status (#REQUIRED)')
213
+
214
+ const weight = parseFloat(record.weight)
215
+ if (isNaN(weight) || weight <= 0 || weight > 1)
216
+ errors.push(`SGML: weight="${record.weight}" out of range (0, 1]`)
217
+
218
+ // Critical domain inaccuracy check (mirrors transformer.dl has_critical_inaccuracy)
219
+ if (record.inaccuracies) {
220
+ for (const inc of record.inaccuracies) {
221
+ if (CRITICAL_DOMAINS.includes(inc.domain)) {
222
+ errors.push(`SGML: critical domain inaccuracy in "${inc.domain}": ${inc.reason}`)
223
+ }
224
+ }
225
+ }
226
+
227
+ return { valid: errors.length === 0, errors }
228
+ }
229
+
230
+ // ── Encode as SGML (matches claimguard.mjs encodeAsSgml) ──────────────────────
231
+
232
+ export function encodeAsSGML(obj, doctype = 'record') {
233
+ const esc = s => String(s)
234
+ .replace(/&/g, '&amp;')
235
+ .replace(/</g, '&lt;')
236
+ .replace(/>/g, '&gt;')
237
+ .replace(/"/g, '&quot;')
238
+
239
+ const lines = [
240
+ `<!DOCTYPE ${doctype} [`,
241
+ ` <!ELEMENT ${doctype} - - (` + Object.keys(obj).join(', ') + `)>`,
242
+ ]
243
+ for (const [k, v] of Object.entries(obj)) {
244
+ lines.push(` <!ELEMENT ${k} - - (#PCDATA)>`)
245
+ }
246
+ lines.push(`]>`)
247
+ lines.push(`<${doctype}>`)
248
+ for (const [k, v] of Object.entries(obj)) {
249
+ lines.push(` <${k}>${esc(v)}</${k}>`)
250
+ }
251
+ lines.push(`</${doctype}>`)
252
+ return lines.join('\n')
253
+ }