gdk9-cli 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- examples/01_analyze.py +28 -0
- examples/02_profile.py +24 -0
- examples/03_assign.py +19 -0
- examples/04_dcg.py +41 -0
- examples/05_tokenize.py +22 -0
- examples/06_synthesize.py +20 -0
- examples/07_api.py +57 -0
- examples/08_kernel.py +35 -0
- examples/09_kernel_search_walkthrough.py +102 -0
- examples/10_conserve_vs_naive.py +100 -0
- gdk9/__init__.py +5 -0
- gdk9/ansi.py +57 -0
- gdk9/cli.py +1186 -0
- gdk9/crdt.py +88 -0
- gdk9/crypto.py +132 -0
- gdk9/data/ninefold.json +40 -0
- gdk9/data/official.example.json +16 -0
- gdk9/data/official.json +132 -0
- gdk9/data/subs.json +436 -0
- gdk9/dcg.py +356 -0
- gdk9/egglog_bridge/__init__.py +6 -0
- gdk9/egglog_bridge/dr_egglog.py +107 -0
- gdk9/egglog_bridge/fallback.py +34 -0
- gdk9/energy.py +211 -0
- gdk9/errors.py +15 -0
- gdk9/fmt.py +212 -0
- gdk9/imply.py +83 -0
- gdk9/io_utils.py +24 -0
- gdk9/kernel/__init__.py +24 -0
- gdk9/kernel/engine.py +123 -0
- gdk9/kernel/errors.py +17 -0
- gdk9/kernel/expression.py +43 -0
- gdk9/kernel/principle.py +43 -0
- gdk9/kernel/proof.py +24 -0
- gdk9/kernel/rule.py +30 -0
- gdk9/kernel/symbol.py +19 -0
- gdk9/kernel_cli.py +195 -0
- gdk9/log.py +30 -0
- gdk9/optimize.py +288 -0
- gdk9/parser.py +35 -0
- gdk9/plugins/__init__.py +9 -0
- gdk9/plugins/loader.py +346 -0
- gdk9/plugins/registry.py +42 -0
- gdk9/principles.py +131 -0
- gdk9/saturate.py +89 -0
- gdk9/state.py +138 -0
- gdk9/subs.py +108 -0
- gdk9/tokenize.py +386 -0
- gdk9/tui.py +115 -0
- gdk9/utilization.py +15 -0
- gdk9_cli-0.3.0.dist-info/METADATA +295 -0
- gdk9_cli-0.3.0.dist-info/RECORD +82 -0
- gdk9_cli-0.3.0.dist-info/WHEEL +5 -0
- gdk9_cli-0.3.0.dist-info/entry_points.txt +3 -0
- gdk9_cli-0.3.0.dist-info/licenses/LICENSE +661 -0
- gdk9_cli-0.3.0.dist-info/top_level.txt +4 -0
- scripts/batch_analyze.py +109 -0
- scripts/export_json.py +125 -0
- scripts/make_zip.py +57 -0
- scripts/profile_compare.py +165 -0
- scripts/watch_file.py +124 -0
- tests/conftest.py +26 -0
- tests/experiment/__init__.py +1 -0
- tests/experiment/test_conserve_vs_naive.py +287 -0
- tests/kernel/test_kernel_energy.py +18 -0
- tests/kernel/test_kernel_rules.py +58 -0
- tests/kernel/test_kernel_search_bound.py +34 -0
- tests/test_crypto.py +36 -0
- tests/test_crypto_secure.py +26 -0
- tests/test_dcg.py +318 -0
- tests/test_dcg_golden.py +67 -0
- tests/test_egglog_bridge.py +41 -0
- tests/test_energy.py +34 -0
- tests/test_kernel_cli_handbook.py +101 -0
- tests/test_optimize.py +21 -0
- tests/test_plugins.py +64 -0
- tests/test_rules.py +43 -0
- tests/test_rules_commit.py +40 -0
- tests/test_rules_reversibility.py +50 -0
- tests/test_state_crdt.py +87 -0
- tests/test_tokenize.py +38 -0
- tests/test_tokenize_metrics.py +33 -0
examples/01_analyze.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Example 01 — analyze: break text into energy units.
|
|
2
|
+
|
|
3
|
+
gdk9 analyze decomposes text from document → paragraph → sentence → word,
|
|
4
|
+
reporting the A1Z26 digital-root energy at each level.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess, sys, os
|
|
7
|
+
|
|
8
|
+
_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
|
9
|
+
|
|
10
|
+
def run(*args):
|
|
11
|
+
result = subprocess.run(
|
|
12
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
13
|
+
capture_output=True, text=True,
|
|
14
|
+
cwd=_ROOT,
|
|
15
|
+
)
|
|
16
|
+
print(result.stdout or result.stderr)
|
|
17
|
+
|
|
18
|
+
# Basic analysis
|
|
19
|
+
print("=== analyze a phrase ===")
|
|
20
|
+
run("--color", "analyze", "Signal 123 ehdxkcit 3245768")
|
|
21
|
+
|
|
22
|
+
# Table format
|
|
23
|
+
print("=== analyze — table format ===")
|
|
24
|
+
run("--color", "analyze", "FWEM dark swan protocol", "--format", "table")
|
|
25
|
+
|
|
26
|
+
# Compare two phrases
|
|
27
|
+
print("=== compare two strings ===")
|
|
28
|
+
run("--color", "compare", "dark swan", "dark swan protocol")
|
examples/02_profile.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Example 02 — profile: energy histogram and DR distribution.
|
|
2
|
+
|
|
3
|
+
profile shows how energy is distributed across digital roots 1-9,
|
|
4
|
+
rendered as a coloured block-bar chart.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess, sys
|
|
7
|
+
|
|
8
|
+
def run(*args):
|
|
9
|
+
result = subprocess.run(
|
|
10
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
11
|
+
capture_output=True, text=True,
|
|
12
|
+
)
|
|
13
|
+
print(result.stdout or result.stderr)
|
|
14
|
+
|
|
15
|
+
texts = [
|
|
16
|
+
"The quick brown fox jumps over the lazy dog",
|
|
17
|
+
"FWEM",
|
|
18
|
+
"dark swan protocol identity",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
for text in texts:
|
|
22
|
+
print(f'=== profile: {text!r} ===')
|
|
23
|
+
run("--color", "profile", text)
|
|
24
|
+
print()
|
examples/03_assign.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Example 03 — assign: per-character energy breakdown.
|
|
2
|
+
|
|
3
|
+
assign lists every character with its individual energy value and DR,
|
|
4
|
+
useful for auditing which letters drive the total energy of a word.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess, sys
|
|
7
|
+
|
|
8
|
+
def run(*args):
|
|
9
|
+
result = subprocess.run(
|
|
10
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
11
|
+
capture_output=True, text=True,
|
|
12
|
+
)
|
|
13
|
+
print(result.stdout or result.stderr)
|
|
14
|
+
|
|
15
|
+
print("=== assign: FWEM ===")
|
|
16
|
+
run("--color", "assign", "FWEM")
|
|
17
|
+
|
|
18
|
+
print("=== assign: dark swan ===")
|
|
19
|
+
run("--color", "assign", "dark swan")
|
examples/04_dcg.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Example 04 — DCG: Directed Cognition Graph.
|
|
2
|
+
|
|
3
|
+
The DCG models letters as nodes in a directed graph with three edge types:
|
|
4
|
+
DC↔AC uppercase ↔ lowercase (cost 0.5 / 2.0)
|
|
5
|
+
within all-pairs within symmetry class (cost 1.0)
|
|
6
|
+
cross energy cycle asym → biph → idemp → invol → asym (cost 2.0)
|
|
7
|
+
|
|
8
|
+
Demonstrates: classify, path, homotopy, shortest, vector, info.
|
|
9
|
+
"""
|
|
10
|
+
import subprocess, sys
|
|
11
|
+
|
|
12
|
+
def run(*args):
|
|
13
|
+
result = subprocess.run(
|
|
14
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
15
|
+
capture_output=True, text=True,
|
|
16
|
+
)
|
|
17
|
+
print(result.stdout or result.stderr)
|
|
18
|
+
|
|
19
|
+
# Symmetry class of individual letters
|
|
20
|
+
print("=== dcg classify ===")
|
|
21
|
+
run("--color", "dcg", "classify", "FWEM")
|
|
22
|
+
|
|
23
|
+
# Full word path — symmetry class + energy per letter
|
|
24
|
+
print("=== dcg path: FWEM ===")
|
|
25
|
+
run("--color", "dcg", "path", "FWEM")
|
|
26
|
+
|
|
27
|
+
# Shortest path between two nodes
|
|
28
|
+
print("=== dcg shortest: F → M ===")
|
|
29
|
+
run("--color", "dcg", "shortest", "F", "M")
|
|
30
|
+
|
|
31
|
+
# Homotopy equivalence: same letters reordered
|
|
32
|
+
print("=== dcg homotopy: FWEM vs MWEF ===")
|
|
33
|
+
run("--color", "dcg", "homotopy", "FWEM", "MWEF")
|
|
34
|
+
|
|
35
|
+
# 4D vector encoding
|
|
36
|
+
print("=== dcg vector: FWEM ===")
|
|
37
|
+
run("--color", "dcg", "vector", "FWEM")
|
|
38
|
+
|
|
39
|
+
# Graph info
|
|
40
|
+
print("=== dcg info ===")
|
|
41
|
+
run("--color", "dcg", "info")
|
examples/05_tokenize.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Example 05 — tokenize: split text by symbolic energy delimiters.
|
|
2
|
+
|
|
3
|
+
Delimiters are characters whose symbol-energy equals the requested level.
|
|
4
|
+
With -F table the output includes per-token DR, energy totals, and a
|
|
5
|
+
metrics footer (DR histogram, top tokens, class breakdown).
|
|
6
|
+
"""
|
|
7
|
+
import subprocess, sys
|
|
8
|
+
|
|
9
|
+
def run(*args):
|
|
10
|
+
result = subprocess.run(
|
|
11
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
12
|
+
capture_output=True, text=True,
|
|
13
|
+
)
|
|
14
|
+
print(result.stdout or result.stderr)
|
|
15
|
+
|
|
16
|
+
text = "alpha|beta:gamma/delta=epsilon"
|
|
17
|
+
|
|
18
|
+
print("=== tokenize at energy-1 delimiters (table) ===")
|
|
19
|
+
run("--color", "tokenize", text, "--energy", "1", "-F", "table")
|
|
20
|
+
|
|
21
|
+
print("=== tokenize JSON payload ===")
|
|
22
|
+
run("tokenize", "FWEM dark-swan protocol", "-F", "json")
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Example 06 — synthesize: build a sigil from an energy profile.
|
|
2
|
+
|
|
3
|
+
synthesize takes a word and renders a bordered sigil with per-letter
|
|
4
|
+
DR coloring, the total SymPhi energy, and the digital root.
|
|
5
|
+
"""
|
|
6
|
+
import subprocess, sys
|
|
7
|
+
|
|
8
|
+
def run(*args):
|
|
9
|
+
result = subprocess.run(
|
|
10
|
+
[sys.executable, "-m", "gdk9.cli", *args],
|
|
11
|
+
capture_output=True, text=True,
|
|
12
|
+
)
|
|
13
|
+
print(result.stdout or result.stderr)
|
|
14
|
+
|
|
15
|
+
words = ["FWEM", "dark", "swan", "protocol"]
|
|
16
|
+
|
|
17
|
+
for word in words:
|
|
18
|
+
print(f"=== synthesize: {word} ===")
|
|
19
|
+
run("--color", "synthesize", word)
|
|
20
|
+
print()
|
examples/07_api.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Example 07 — Python API: use gdk9 modules directly.
|
|
2
|
+
|
|
3
|
+
All CLI commands are thin wrappers around importable Python functions.
|
|
4
|
+
This example exercises the core API without subprocess.
|
|
5
|
+
"""
|
|
6
|
+
import sys, os
|
|
7
|
+
# Allow running from the examples/ directory
|
|
8
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
9
|
+
|
|
10
|
+
from gdk9.energy import string_energy, char_energy
|
|
11
|
+
from gdk9.dcg import (
|
|
12
|
+
get_dcg, symmetry_class, sym_energy,
|
|
13
|
+
word_sym_energy, homotopy_equivalent,
|
|
14
|
+
)
|
|
15
|
+
from gdk9.fmt import fmt_class, fmt_dr, fmt_float_e, Box, section, kv
|
|
16
|
+
from gdk9.principles import Principle
|
|
17
|
+
|
|
18
|
+
principle = Principle.default()
|
|
19
|
+
|
|
20
|
+
# ── Energy model ──────────────────────────────────────────────────────────────
|
|
21
|
+
print(section("energy model", enabled=False))
|
|
22
|
+
|
|
23
|
+
for word in ["FWEM", "dark", "swan"]:
|
|
24
|
+
total, dr = string_energy(word, principle)
|
|
25
|
+
print(f' {word:<10} total={total:>6} dr={dr}')
|
|
26
|
+
|
|
27
|
+
# ── DCG ───────────────────────────────────────────────────────────────────────
|
|
28
|
+
print()
|
|
29
|
+
print(section("dcg", enabled=False))
|
|
30
|
+
|
|
31
|
+
g = get_dcg()
|
|
32
|
+
print(f' nodes={g.node_count()} edges={g.edge_count()}')
|
|
33
|
+
|
|
34
|
+
for ch in "FWEM":
|
|
35
|
+
cls = symmetry_class(ch)
|
|
36
|
+
e = sym_energy(ch)
|
|
37
|
+
print(f' {ch} {cls:<12} e={e:.4f}')
|
|
38
|
+
|
|
39
|
+
print()
|
|
40
|
+
path = g.shortest_path("F", "M")
|
|
41
|
+
print(f' shortest F→M: {" → ".join(path)}')
|
|
42
|
+
|
|
43
|
+
equiv, detail = homotopy_equivalent("FWEM", "MWEF", tol=1.0)
|
|
44
|
+
print(f' homotopy FWEM≅MWEF: {equiv} '
|
|
45
|
+
f'(Δe={detail["energy_diff"]:.4f} dist={detail["vector_distance"]:.4f})')
|
|
46
|
+
|
|
47
|
+
# ── fmt helpers ───────────────────────────────────────────────────────────────
|
|
48
|
+
print()
|
|
49
|
+
print(section("fmt — Box table", enabled=False))
|
|
50
|
+
|
|
51
|
+
box = Box(["char", "class", "energy", "dr"], col_align=["c", "l", "r", "c"], enabled=False)
|
|
52
|
+
for ch in "FWEM":
|
|
53
|
+
e = sym_energy(ch)
|
|
54
|
+
cls = symmetry_class(ch)
|
|
55
|
+
_, dr = string_energy(ch, principle)
|
|
56
|
+
box.row([ch, cls, f"{e:.4f}", str(dr)])
|
|
57
|
+
print(box.render())
|
examples/08_kernel.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Example 08 — Pure kernel smoke: evaluate, fuse, and bounded search.
|
|
2
|
+
|
|
3
|
+
The kernel package has no CLI/state/plugin I/O. This script uses the library
|
|
4
|
+
surface (and optionally the thin ``gdk9 kernel`` CLI adapter) for research.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
11
|
+
|
|
12
|
+
from gdk9.kernel import Expression, ImplicationEngine, KernelPrinciple
|
|
13
|
+
from gdk9.kernel.engine import fusion_rule
|
|
14
|
+
from gdk9.kernel.symbol import Symbol
|
|
15
|
+
from gdk9.kernel_cli import expression_payload, judgment_payload, proof_steps_payload, principle_to_kernel
|
|
16
|
+
from gdk9.principles import Principle
|
|
17
|
+
|
|
18
|
+
principle = Principle.default()
|
|
19
|
+
kp = principle_to_kernel(principle)
|
|
20
|
+
engine = ImplicationEngine(kp, (fusion_rule(),))
|
|
21
|
+
|
|
22
|
+
# Evaluate
|
|
23
|
+
expr = Expression.from_text("ABC", kp)
|
|
24
|
+
total, root = engine.evaluate(expr)
|
|
25
|
+
print(json.dumps({"eval": {"expression": expression_payload(expr), "total": total, "digital_root": root}}, indent=2))
|
|
26
|
+
|
|
27
|
+
# Apply fuse
|
|
28
|
+
source = Expression.from_names(["A", "B"], kp)
|
|
29
|
+
judgment = engine.apply("fuse", source)
|
|
30
|
+
print(json.dumps({"apply": judgment_payload(judgment)}, indent=2))
|
|
31
|
+
|
|
32
|
+
# Bounded search A,B -> AB
|
|
33
|
+
target = Expression((Symbol("AB", source.total_energy()),))
|
|
34
|
+
path = engine.infer(source, target, max_depth=2)
|
|
35
|
+
print(json.dumps({"search": {"found": path is not None, "steps": proof_steps_payload(path or ())}}, indent=2))
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Example 09 — Kernel search walkthrough (handbook voice).
|
|
2
|
+
|
|
3
|
+
Three short scenes. Each conserves energy. Each can fail if the depth bound
|
|
4
|
+
is too tight or the target energy does not match — that failure is the point.
|
|
5
|
+
|
|
6
|
+
Run::
|
|
7
|
+
|
|
8
|
+
python examples/09_kernel_search_walkthrough.py
|
|
9
|
+
|
|
10
|
+
Or mirror with the CLI::
|
|
11
|
+
|
|
12
|
+
gdk9 kernel eval A B C
|
|
13
|
+
gdk9 kernel apply fuse A B
|
|
14
|
+
gdk9 kernel search A B --target AB --max-depth 2 --rules fuse
|
|
15
|
+
gdk9 kernel apply split AB --parts A,B --energies 1,2
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
24
|
+
|
|
25
|
+
from gdk9.kernel import Expression, ImplicationEngine
|
|
26
|
+
from gdk9.kernel.engine import fusion_rule, split_rule
|
|
27
|
+
from gdk9.kernel.symbol import Symbol
|
|
28
|
+
from gdk9.kernel_cli import (
|
|
29
|
+
judgment_payload,
|
|
30
|
+
principle_to_kernel,
|
|
31
|
+
proof_steps_payload,
|
|
32
|
+
)
|
|
33
|
+
from gdk9.principles import Principle
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def scene(title: str, payload: dict) -> None:
|
|
37
|
+
print(f"\n## {title}")
|
|
38
|
+
print(json.dumps(payload, indent=2, ensure_ascii=False))
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def main() -> int:
|
|
42
|
+
kp = principle_to_kernel(Principle.default())
|
|
43
|
+
fuse = fusion_rule()
|
|
44
|
+
a_e = Expression.from_names(["A"], kp).total_energy()
|
|
45
|
+
b_e = Expression.from_names(["B"], kp).total_energy()
|
|
46
|
+
split = split_rule(("A", "B"), (a_e, b_e))
|
|
47
|
+
|
|
48
|
+
# Scene 1 — weigh the letters (eval)
|
|
49
|
+
eng = ImplicationEngine(kp, (fuse,))
|
|
50
|
+
abc = Expression.from_names(["A", "B", "C"], kp)
|
|
51
|
+
total, root = eng.evaluate(abc)
|
|
52
|
+
scene(
|
|
53
|
+
"1. Weigh — eval ABC",
|
|
54
|
+
{
|
|
55
|
+
"names": list(abc.names()),
|
|
56
|
+
"total_energy": total,
|
|
57
|
+
"digital_root": root,
|
|
58
|
+
"note": "A=1, B=2, C=3 under the default principle; sum=6, DR=6.",
|
|
59
|
+
},
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Scene 2 — one conserved fuse, then find it by bounded search
|
|
63
|
+
src = Expression.from_names(["A", "B"], kp)
|
|
64
|
+
judgment = eng.apply("fuse", src)
|
|
65
|
+
target = Expression((Symbol("AB", src.total_energy()),))
|
|
66
|
+
path = eng.infer(src, target, max_depth=2)
|
|
67
|
+
scene(
|
|
68
|
+
"2. Fuse — then search A,B → AB (max_depth=2)",
|
|
69
|
+
{
|
|
70
|
+
"apply": judgment_payload(judgment),
|
|
71
|
+
"search_found": path is not None,
|
|
72
|
+
"steps": proof_steps_payload(path or ()),
|
|
73
|
+
"note": "Search is not magic: it is bounded BFS over conserving rules.",
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Scene 3 — reverse the arrow (split) with matching energies
|
|
78
|
+
eng2 = ImplicationEngine(kp, (fuse, split))
|
|
79
|
+
ab = judgment.after
|
|
80
|
+
back = eng2.apply("split", ab)
|
|
81
|
+
scene(
|
|
82
|
+
"3. Split — AB → A,B with matching energies (conserved)",
|
|
83
|
+
{
|
|
84
|
+
"judgment": judgment_payload(back),
|
|
85
|
+
"note": "Split needs explicit part energies that sum to the whole.",
|
|
86
|
+
},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Scene 4 — deliberate miss (prove it can fail)
|
|
90
|
+
miss = eng.infer(src, Expression((Symbol("ZZ", 99.0),)), max_depth=2)
|
|
91
|
+
scene(
|
|
92
|
+
"4. Miss — impossible target (energy 99) within depth 2",
|
|
93
|
+
{
|
|
94
|
+
"found": miss is not None,
|
|
95
|
+
"note": "A honest research tool must be able to say no.",
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Example 10 — Conserved search vs naive name-join/split (research move 4).
|
|
2
|
+
|
|
3
|
+
Prints a short JSON summary. Full fail bar lives in pytest::
|
|
4
|
+
|
|
5
|
+
python -m pytest -q tests/experiment/test_conserve_vs_naive.py
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
|
14
|
+
|
|
15
|
+
from gdk9.kernel import Expression, ImplicationEngine, KernelPrinciple
|
|
16
|
+
from gdk9.kernel.engine import fusion_rule, split_rule
|
|
17
|
+
from gdk9.kernel.symbol import Symbol
|
|
18
|
+
from tests.experiment.test_conserve_vs_naive import (
|
|
19
|
+
MISMATCHED_AB_ENERGY,
|
|
20
|
+
MISMATCHED_A_ENERGY,
|
|
21
|
+
MISMATCHED_B_ENERGY,
|
|
22
|
+
conserved_kernel_proof,
|
|
23
|
+
naive_join_search,
|
|
24
|
+
naive_split_search,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main() -> int:
|
|
29
|
+
principle = KernelPrinciple.default()
|
|
30
|
+
ea = principle.energy_of("A")
|
|
31
|
+
eb = principle.energy_of("B")
|
|
32
|
+
source = Expression.from_names(["A", "B"], principle)
|
|
33
|
+
conserved_target = Expression((Symbol("AB", source.total_energy()),))
|
|
34
|
+
mismatched = Expression((Symbol("AB", MISMATCHED_AB_ENERGY),))
|
|
35
|
+
fuse_engine = ImplicationEngine(principle, (fusion_rule(),))
|
|
36
|
+
|
|
37
|
+
ab_source = Expression((Symbol("AB", ea + eb),))
|
|
38
|
+
split_target = Expression.from_names(["A", "B"], principle)
|
|
39
|
+
split_mismatched = Expression(
|
|
40
|
+
(Symbol("A", MISMATCHED_A_ENERGY), Symbol("B", MISMATCHED_B_ENERGY))
|
|
41
|
+
)
|
|
42
|
+
split_engine = ImplicationEngine(principle, (split_rule(("A", "B"), (ea, eb)),))
|
|
43
|
+
|
|
44
|
+
conserved_path = conserved_kernel_proof(fuse_engine, source, conserved_target, max_depth=2)
|
|
45
|
+
mismatch_kernel = conserved_kernel_proof(fuse_engine, source, mismatched, max_depth=2)
|
|
46
|
+
naive_ok = naive_join_search(source.names(), ("AB",), max_depth=2)
|
|
47
|
+
|
|
48
|
+
split_path = conserved_kernel_proof(split_engine, ab_source, split_target, max_depth=2)
|
|
49
|
+
split_mismatch_kernel = conserved_kernel_proof(
|
|
50
|
+
split_engine, ab_source, split_mismatched, max_depth=2
|
|
51
|
+
)
|
|
52
|
+
naive_split_ok = naive_split_search(ab_source.names(), ("A", "B"), max_depth=2)
|
|
53
|
+
|
|
54
|
+
summary = {
|
|
55
|
+
"experiment": "conserve_vs_naive",
|
|
56
|
+
"approach": "A",
|
|
57
|
+
"move": 4,
|
|
58
|
+
"claim": "beats naive join/split on proof validity under conservation",
|
|
59
|
+
"source_names": list(source.names()),
|
|
60
|
+
"source_energy": source.total_energy(),
|
|
61
|
+
"conserved_ab": {
|
|
62
|
+
"kernel_found": conserved_path is not None,
|
|
63
|
+
"steps": len(conserved_path or ()),
|
|
64
|
+
"all_conserved": bool(
|
|
65
|
+
conserved_path
|
|
66
|
+
and all(s.judgment.conserved for s in conserved_path)
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
"mismatched_ab_energy": MISMATCHED_AB_ENERGY,
|
|
70
|
+
"mismatched_ab": {
|
|
71
|
+
"naive_found": naive_ok is not None,
|
|
72
|
+
"kernel_found": mismatch_kernel is not None,
|
|
73
|
+
},
|
|
74
|
+
"conserved_split": {
|
|
75
|
+
"kernel_found": split_path is not None,
|
|
76
|
+
"steps": len(split_path or ()),
|
|
77
|
+
"all_conserved": bool(
|
|
78
|
+
split_path and all(s.judgment.conserved for s in split_path)
|
|
79
|
+
),
|
|
80
|
+
},
|
|
81
|
+
"mismatched_split": {
|
|
82
|
+
"naive_found": naive_split_ok is not None,
|
|
83
|
+
"kernel_found": split_mismatch_kernel is not None,
|
|
84
|
+
"part_energies": [MISMATCHED_A_ENERGY, MISMATCHED_B_ENERGY],
|
|
85
|
+
},
|
|
86
|
+
"validity_beat_fuse": naive_ok is not None and mismatch_kernel is None,
|
|
87
|
+
"validity_beat_split": naive_split_ok is not None and split_mismatch_kernel is None,
|
|
88
|
+
}
|
|
89
|
+
print(json.dumps(summary, indent=2))
|
|
90
|
+
ok = (
|
|
91
|
+
summary["validity_beat_fuse"]
|
|
92
|
+
and summary["validity_beat_split"]
|
|
93
|
+
and summary["conserved_ab"]["kernel_found"]
|
|
94
|
+
and summary["conserved_split"]["kernel_found"]
|
|
95
|
+
)
|
|
96
|
+
return 0 if ok else 1
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
raise SystemExit(main())
|
gdk9/__init__.py
ADDED
gdk9/ansi.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
|
|
8
|
+
|
|
9
|
+
_CODES: dict[str, str] = {
|
|
10
|
+
# styles
|
|
11
|
+
"bold": "\033[1m",
|
|
12
|
+
"dim": "\033[2m",
|
|
13
|
+
"italic": "\033[3m",
|
|
14
|
+
"underline": "\033[4m",
|
|
15
|
+
"reverse": "\033[7m",
|
|
16
|
+
# standard colours
|
|
17
|
+
"red": "\033[31m",
|
|
18
|
+
"green": "\033[32m",
|
|
19
|
+
"yellow": "\033[33m",
|
|
20
|
+
"blue": "\033[34m",
|
|
21
|
+
"magenta": "\033[35m",
|
|
22
|
+
"cyan": "\033[36m",
|
|
23
|
+
"white": "\033[37m",
|
|
24
|
+
# bright variants
|
|
25
|
+
"bright_black": "\033[90m",
|
|
26
|
+
"bright_red": "\033[91m",
|
|
27
|
+
"bright_green": "\033[92m",
|
|
28
|
+
"bright_yellow": "\033[93m",
|
|
29
|
+
"bright_blue": "\033[94m",
|
|
30
|
+
"bright_magenta": "\033[95m",
|
|
31
|
+
"bright_cyan": "\033[96m",
|
|
32
|
+
"bright_white": "\033[97m",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_RESET = "\033[0m"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def supports_color() -> bool:
|
|
39
|
+
if os.getenv("NO_COLOR") == "1":
|
|
40
|
+
return False
|
|
41
|
+
term = os.getenv("TERM", "")
|
|
42
|
+
if term in ("dumb", ""):
|
|
43
|
+
return False
|
|
44
|
+
return sys.stdout.isatty()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def colorize(text: str, color: str | None, enabled: bool) -> str:
|
|
48
|
+
if not enabled or not color:
|
|
49
|
+
return text
|
|
50
|
+
code = _CODES.get(color, "")
|
|
51
|
+
return f"{code}{text}{_RESET}" if code else text
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def strip_ansi(s: str) -> str:
|
|
55
|
+
"""Remove all ANSI escape codes from *s*."""
|
|
56
|
+
return _ANSI_RE.sub("", s)
|
|
57
|
+
|