push from SNAPKITTYWEST/pocketlearn
Browse files- LICENSE.tri +101 -0
- Makefile +63 -0
- README.md +126 -0
- corpus_to_background.xslt +18 -0
- corpus_to_forth.xslt +56 -0
- corpus_tokens.xml +123 -0
- generated_corpus_induced.fth +48 -0
- ontology.asp +14 -0
- ontology.dtd +35 -0
- ontology.xml +63 -0
- ontology_induced.xml +31 -0
- ontology_induction_generated.pl +143 -0
- ontology_to_asp.xslt +13 -0
- ontology_to_induction.xslt +58 -0
- sample_corpus.txt +7 -0
- sgml_validator.mjs +253 -0
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 <> IF NIP NIP EXIT THEN
|
| 20 |
+
DROP VOCAB-SIZE @ DUP >R VOCAB R@ 32 * + PLACE
|
| 21 |
+
R@ 1+ VOCAB-SIZE ! R> ;
|
| 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 <> 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, '&')
|
| 235 |
+
.replace(/</g, '<')
|
| 236 |
+
.replace(/>/g, '>')
|
| 237 |
+
.replace(/"/g, '"')
|
| 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 |
+
}
|