eyelang 1.7.15 → 1.7.17
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.
- package/README.md +1 -0
- package/docs/compact-reasoning-workbench.md +35 -0
- package/examples/deep-taxonomy-10.eye +4 -13
- package/examples/deep-taxonomy-100.eye +4 -13
- package/examples/deep-taxonomy-1000.eye +4 -13
- package/examples/deep-taxonomy-10000.eye +4 -22
- package/examples/deep-taxonomy-100000.eye +4 -112
- package/examples/output/deep-taxonomy-10.eye +1 -1
- package/examples/output/deep-taxonomy-100.eye +1 -1
- package/examples/output/deep-taxonomy-1000.eye +1 -1
- package/examples/output/deep-taxonomy-10000.eye +1 -1
- package/examples/output/deep-taxonomy-100000.eye +1 -1
- package/package.json +1 -1
- package/src/builtins/control.js +11 -2
- package/src/cli.js +3 -5
- package/src/solver.js +89 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ console.log(result.stdout);
|
|
|
49
49
|
- [Playground](https://eyereasoner.github.io/eyelang/playground)
|
|
50
50
|
- [Guide](docs/guide.md)
|
|
51
51
|
- [Language reference](docs/language-reference.md)
|
|
52
|
+
- [A Compact Reasoning Workbench](docs/compact-reasoning-workbench.md)
|
|
52
53
|
|
|
53
54
|
For local browser use, serve the checkout first so the playground can load ES modules and example files:
|
|
54
55
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# A Compact Reasoning Workbench
|
|
2
|
+
|
|
3
|
+
Eyelang began as a small thing: a few facts, a few rules, a way to ask a question and get an answer.
|
|
4
|
+
|
|
5
|
+
At first it solved the familiar problems. Paths through graphs. Ancestors in families. Tiny proofs that fit on one screen. The early examples in `examples/` keep that beginning visible: they show the core idiom before anything becomes exotic. Facts describe a world, rules extend it, goals ask for visible consequences, and answers come back as ordinary terms.
|
|
6
|
+
|
|
7
|
+
Then the examples started to widen.
|
|
8
|
+
|
|
9
|
+
Mathematical programs turned recurrence, tabling, aggregation, and finite search into executable relations. Fibonacci numbers, Collatz traces, integer partitions, Catalan numbers, binomial identities, Stirling and Bell numbers, graph paths, and scheduling problems made performance visible without hiding the logic behind a host-language library.
|
|
10
|
+
|
|
11
|
+
Knowledge-modeling examples pulled the same syntax in a different direction. Policies, GDPR-style compliance, access control, clinical screening, deontic rules, purpose mapping, alignment flows, and context audits were not mainly numerical problems. They were problems of explanation, qualification, exception, and traceability. In those cases Eyelang stayed close to the vocabulary of the domain.
|
|
12
|
+
|
|
13
|
+
Science and engineering examples stretched the language again. Filters, beams, control systems, kinetics, gas laws, gradients, coding theory, and optimization showed that structured calculation could live inside a larger rule-based argument. The programs remained small enough to read, but substantial enough to test the engine.
|
|
14
|
+
|
|
15
|
+
Then came examples that looked more like systems research than toy logic programs. Markov Logic Network-style scoring represented weighted possible worlds while keeping the probability model explicit. Type inference, abstract interpretation, pointer analysis, SAT solving, truth maintenance, and register allocation showed that programming-language and analysis problems could be written as compact theories rather than opaque procedures.
|
|
16
|
+
|
|
17
|
+
At the far edge were the deliberately uncomfortable examples: partial evaluation, Knuth-Bendix completion, CDCL-style SAT solving, and bounded equality saturation. They did not pretend that Eyelang was a mutable union-find library, an industrial optimizer, or a replacement for every specialized solver. They were useful precisely because they marked the boundary. Some problems had to be bounded. Some had to be expressed carefully. Some revealed where a small logic engine ends and another tool should take over.
|
|
18
|
+
|
|
19
|
+
That made `examples/` more than a gallery. It became executable documentation, regression evidence, and a map of the language's range. Each file was a compact theory with an expected output that could be run, inspected, and kept honest by the test suite.
|
|
20
|
+
|
|
21
|
+
The surprise was not that every hard problem became easy. They did not.
|
|
22
|
+
|
|
23
|
+
The surprise was that the same small core kept reaching.
|
|
24
|
+
|
|
25
|
+
Across mathematics, knowledge modeling, science, program analysis, symbolic reasoning, optimization, explanation, and search, Eyelang stayed recognizable. The programs were not black boxes. They were little theories: readable, testable, and executable. When performance mattered, tabling and careful formulations often carried them farther than expected.
|
|
26
|
+
|
|
27
|
+
By the end, Eyelang no longer looked like a toy language with a few clever demos.
|
|
28
|
+
|
|
29
|
+
It looked like a compact reasoning workbench.
|
|
30
|
+
|
|
31
|
+
Small enough to understand.
|
|
32
|
+
|
|
33
|
+
Expressive enough to surprise.
|
|
34
|
+
|
|
35
|
+
Fast enough to take seriously.
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
% =============================================================================================================================
|
|
2
|
-
% Deep Taxonomy - depth 10 - expanded N3-style eyelang
|
|
2
|
+
% Deep Taxonomy - depth 10 - expanded N3-style eyelang
|
|
3
3
|
%
|
|
4
|
-
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain.
|
|
5
|
-
%
|
|
6
|
-
% can prove terminal/report goals without overflowing the JavaScript call stack.
|
|
7
|
-
% Report/check rules use once/1 so successful checks do not backtrack into a
|
|
8
|
-
% long adjacent-only proof after a shorter checkpoint proof has succeeded.
|
|
4
|
+
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
|
|
5
|
+
% the next taxonomy class together with two side labels.
|
|
9
6
|
% =============================================================================================================================
|
|
10
7
|
|
|
11
8
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
@@ -27,12 +24,6 @@ a(ind, n0).
|
|
|
27
24
|
is(test, true) :- once(a(ind, a2)).
|
|
28
25
|
a(?x, a2) :- a(?x, n10).
|
|
29
26
|
|
|
30
|
-
% Redundant checkpoint accelerators.
|
|
31
|
-
|
|
32
|
-
a(?x, n10) :- a(?x, n0).
|
|
33
|
-
a(?x, n4) :- a(?x, n0).
|
|
34
|
-
a(?x, n9) :- a(?x, n0).
|
|
35
|
-
|
|
36
27
|
% Adjacent N3-style taxonomy rules.
|
|
37
28
|
|
|
38
29
|
a(?x, n1) :- a(?x, n0).
|
|
@@ -98,7 +89,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
|
|
|
98
89
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10 and then as a2.") :-
|
|
99
90
|
once(is(test, true)).
|
|
100
91
|
|
|
101
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain
|
|
92
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
|
|
102
93
|
once(a(ind, a2)),
|
|
103
94
|
once(is(test, true)).
|
|
104
95
|
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
% =============================================================================================================================
|
|
2
|
-
% Deep Taxonomy - depth 100 - expanded N3-style eyelang
|
|
2
|
+
% Deep Taxonomy - depth 100 - expanded N3-style eyelang
|
|
3
3
|
%
|
|
4
|
-
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain.
|
|
5
|
-
%
|
|
6
|
-
% can prove terminal/report goals without overflowing the JavaScript call stack.
|
|
7
|
-
% Report/check rules use once/1 so successful checks do not backtrack into a
|
|
8
|
-
% long adjacent-only proof after a shorter checkpoint proof has succeeded.
|
|
4
|
+
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
|
|
5
|
+
% the next taxonomy class together with two side labels.
|
|
9
6
|
% =============================================================================================================================
|
|
10
7
|
|
|
11
8
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
@@ -27,12 +24,6 @@ a(ind, n0).
|
|
|
27
24
|
is(test, true) :- once(a(ind, a2)).
|
|
28
25
|
a(?x, a2) :- a(?x, n100).
|
|
29
26
|
|
|
30
|
-
% Redundant checkpoint accelerators.
|
|
31
|
-
|
|
32
|
-
a(?x, n100) :- a(?x, n0).
|
|
33
|
-
a(?x, n49) :- a(?x, n0).
|
|
34
|
-
a(?x, n99) :- a(?x, n0).
|
|
35
|
-
|
|
36
27
|
% Adjacent N3-style taxonomy rules.
|
|
37
28
|
|
|
38
29
|
a(?x, n1) :- a(?x, n0).
|
|
@@ -368,7 +359,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
|
|
|
368
359
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100 and then as a2.") :-
|
|
369
360
|
once(is(test, true)).
|
|
370
361
|
|
|
371
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain
|
|
362
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
|
|
372
363
|
once(a(ind, a2)),
|
|
373
364
|
once(is(test, true)).
|
|
374
365
|
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
% =============================================================================================================================
|
|
2
|
-
% Deep Taxonomy - depth 1000 - expanded N3-style eyelang
|
|
2
|
+
% Deep Taxonomy - depth 1000 - expanded N3-style eyelang
|
|
3
3
|
%
|
|
4
|
-
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain.
|
|
5
|
-
%
|
|
6
|
-
% can prove terminal/report goals without overflowing the JavaScript call stack.
|
|
7
|
-
% Report/check rules use once/1 so successful checks do not backtrack into a
|
|
8
|
-
% long adjacent-only proof after a shorter checkpoint proof has succeeded.
|
|
4
|
+
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
|
|
5
|
+
% the next taxonomy class together with two side labels.
|
|
9
6
|
% =============================================================================================================================
|
|
10
7
|
|
|
11
8
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
@@ -27,12 +24,6 @@ a(ind, n0).
|
|
|
27
24
|
is(test, true) :- once(a(ind, a2)).
|
|
28
25
|
a(?x, a2) :- a(?x, n1000).
|
|
29
26
|
|
|
30
|
-
% Redundant checkpoint accelerators.
|
|
31
|
-
|
|
32
|
-
a(?x, n1000) :- a(?x, n0).
|
|
33
|
-
a(?x, n499) :- a(?x, n0).
|
|
34
|
-
a(?x, n999) :- a(?x, n0).
|
|
35
|
-
|
|
36
27
|
% Adjacent N3-style taxonomy rules.
|
|
37
28
|
|
|
38
29
|
a(?x, n1) :- a(?x, n0).
|
|
@@ -3068,7 +3059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
|
|
|
3068
3059
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n1000 and then as a2.") :-
|
|
3069
3060
|
once(is(test, true)).
|
|
3070
3061
|
|
|
3071
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain
|
|
3062
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
|
|
3072
3063
|
once(a(ind, a2)),
|
|
3073
3064
|
once(is(test, true)).
|
|
3074
3065
|
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
% =============================================================================================================================
|
|
2
|
-
% Deep Taxonomy - depth 10000 - expanded N3-style eyelang
|
|
2
|
+
% Deep Taxonomy - depth 10000 - expanded N3-style eyelang
|
|
3
3
|
%
|
|
4
|
-
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain.
|
|
5
|
-
%
|
|
6
|
-
% can prove terminal/report goals without overflowing the JavaScript call stack.
|
|
7
|
-
% Report/check rules use once/1 so successful checks do not backtrack into a
|
|
8
|
-
% long adjacent-only proof after a shorter checkpoint proof has succeeded.
|
|
4
|
+
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
|
|
5
|
+
% the next taxonomy class together with two side labels.
|
|
9
6
|
% =============================================================================================================================
|
|
10
7
|
|
|
11
8
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
@@ -27,21 +24,6 @@ a(ind, n0).
|
|
|
27
24
|
is(test, true) :- once(a(ind, a2)).
|
|
28
25
|
a(?x, a2) :- a(?x, n10000).
|
|
29
26
|
|
|
30
|
-
% Redundant checkpoint accelerators.
|
|
31
|
-
|
|
32
|
-
a(?x, n1000) :- a(?x, n0).
|
|
33
|
-
a(?x, n2000) :- a(?x, n1000).
|
|
34
|
-
a(?x, n3000) :- a(?x, n2000).
|
|
35
|
-
a(?x, n4000) :- a(?x, n3000).
|
|
36
|
-
a(?x, n5000) :- a(?x, n4000).
|
|
37
|
-
a(?x, n6000) :- a(?x, n5000).
|
|
38
|
-
a(?x, n7000) :- a(?x, n6000).
|
|
39
|
-
a(?x, n8000) :- a(?x, n7000).
|
|
40
|
-
a(?x, n9000) :- a(?x, n8000).
|
|
41
|
-
a(?x, n10000) :- a(?x, n9000).
|
|
42
|
-
a(?x, n4999) :- a(?x, n4000).
|
|
43
|
-
a(?x, n9999) :- a(?x, n9000).
|
|
44
|
-
|
|
45
27
|
% Adjacent N3-style taxonomy rules.
|
|
46
28
|
|
|
47
29
|
a(?x, n1) :- a(?x, n0).
|
|
@@ -30077,7 +30059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
|
|
|
30077
30059
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10000 and then as a2.") :-
|
|
30078
30060
|
once(is(test, true)).
|
|
30079
30061
|
|
|
30080
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain
|
|
30062
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
|
|
30081
30063
|
once(a(ind, a2)),
|
|
30082
30064
|
once(is(test, true)).
|
|
30083
30065
|
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
% =============================================================================================================================
|
|
2
|
-
% Deep Taxonomy - depth 100000 - expanded N3-style eyelang
|
|
2
|
+
% Deep Taxonomy - depth 100000 - expanded N3-style eyelang
|
|
3
3
|
%
|
|
4
|
-
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain.
|
|
5
|
-
%
|
|
6
|
-
% can prove terminal/report goals without overflowing the JavaScript call stack.
|
|
7
|
-
% Report/check rules use once/1 so successful checks do not backtrack into a
|
|
8
|
-
% long adjacent-only proof after a shorter checkpoint proof has succeeded.
|
|
4
|
+
% Adjacent rules mirror the Eyeling N3 deep-taxonomy chain. Each step derives
|
|
5
|
+
% the next taxonomy class together with two side labels.
|
|
9
6
|
% =============================================================================================================================
|
|
10
7
|
|
|
11
8
|
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
@@ -27,111 +24,6 @@ a(ind, n0).
|
|
|
27
24
|
is(test, true) :- once(a(ind, a2)).
|
|
28
25
|
a(?x, a2) :- a(?x, n100000).
|
|
29
26
|
|
|
30
|
-
% Redundant checkpoint accelerators.
|
|
31
|
-
|
|
32
|
-
a(?x, n1000) :- a(?x, n0).
|
|
33
|
-
a(?x, n2000) :- a(?x, n1000).
|
|
34
|
-
a(?x, n3000) :- a(?x, n2000).
|
|
35
|
-
a(?x, n4000) :- a(?x, n3000).
|
|
36
|
-
a(?x, n5000) :- a(?x, n4000).
|
|
37
|
-
a(?x, n6000) :- a(?x, n5000).
|
|
38
|
-
a(?x, n7000) :- a(?x, n6000).
|
|
39
|
-
a(?x, n8000) :- a(?x, n7000).
|
|
40
|
-
a(?x, n9000) :- a(?x, n8000).
|
|
41
|
-
a(?x, n10000) :- a(?x, n9000).
|
|
42
|
-
a(?x, n11000) :- a(?x, n10000).
|
|
43
|
-
a(?x, n12000) :- a(?x, n11000).
|
|
44
|
-
a(?x, n13000) :- a(?x, n12000).
|
|
45
|
-
a(?x, n14000) :- a(?x, n13000).
|
|
46
|
-
a(?x, n15000) :- a(?x, n14000).
|
|
47
|
-
a(?x, n16000) :- a(?x, n15000).
|
|
48
|
-
a(?x, n17000) :- a(?x, n16000).
|
|
49
|
-
a(?x, n18000) :- a(?x, n17000).
|
|
50
|
-
a(?x, n19000) :- a(?x, n18000).
|
|
51
|
-
a(?x, n20000) :- a(?x, n19000).
|
|
52
|
-
a(?x, n21000) :- a(?x, n20000).
|
|
53
|
-
a(?x, n22000) :- a(?x, n21000).
|
|
54
|
-
a(?x, n23000) :- a(?x, n22000).
|
|
55
|
-
a(?x, n24000) :- a(?x, n23000).
|
|
56
|
-
a(?x, n25000) :- a(?x, n24000).
|
|
57
|
-
a(?x, n26000) :- a(?x, n25000).
|
|
58
|
-
a(?x, n27000) :- a(?x, n26000).
|
|
59
|
-
a(?x, n28000) :- a(?x, n27000).
|
|
60
|
-
a(?x, n29000) :- a(?x, n28000).
|
|
61
|
-
a(?x, n30000) :- a(?x, n29000).
|
|
62
|
-
a(?x, n31000) :- a(?x, n30000).
|
|
63
|
-
a(?x, n32000) :- a(?x, n31000).
|
|
64
|
-
a(?x, n33000) :- a(?x, n32000).
|
|
65
|
-
a(?x, n34000) :- a(?x, n33000).
|
|
66
|
-
a(?x, n35000) :- a(?x, n34000).
|
|
67
|
-
a(?x, n36000) :- a(?x, n35000).
|
|
68
|
-
a(?x, n37000) :- a(?x, n36000).
|
|
69
|
-
a(?x, n38000) :- a(?x, n37000).
|
|
70
|
-
a(?x, n39000) :- a(?x, n38000).
|
|
71
|
-
a(?x, n40000) :- a(?x, n39000).
|
|
72
|
-
a(?x, n41000) :- a(?x, n40000).
|
|
73
|
-
a(?x, n42000) :- a(?x, n41000).
|
|
74
|
-
a(?x, n43000) :- a(?x, n42000).
|
|
75
|
-
a(?x, n44000) :- a(?x, n43000).
|
|
76
|
-
a(?x, n45000) :- a(?x, n44000).
|
|
77
|
-
a(?x, n46000) :- a(?x, n45000).
|
|
78
|
-
a(?x, n47000) :- a(?x, n46000).
|
|
79
|
-
a(?x, n48000) :- a(?x, n47000).
|
|
80
|
-
a(?x, n49000) :- a(?x, n48000).
|
|
81
|
-
a(?x, n50000) :- a(?x, n49000).
|
|
82
|
-
a(?x, n51000) :- a(?x, n50000).
|
|
83
|
-
a(?x, n52000) :- a(?x, n51000).
|
|
84
|
-
a(?x, n53000) :- a(?x, n52000).
|
|
85
|
-
a(?x, n54000) :- a(?x, n53000).
|
|
86
|
-
a(?x, n55000) :- a(?x, n54000).
|
|
87
|
-
a(?x, n56000) :- a(?x, n55000).
|
|
88
|
-
a(?x, n57000) :- a(?x, n56000).
|
|
89
|
-
a(?x, n58000) :- a(?x, n57000).
|
|
90
|
-
a(?x, n59000) :- a(?x, n58000).
|
|
91
|
-
a(?x, n60000) :- a(?x, n59000).
|
|
92
|
-
a(?x, n61000) :- a(?x, n60000).
|
|
93
|
-
a(?x, n62000) :- a(?x, n61000).
|
|
94
|
-
a(?x, n63000) :- a(?x, n62000).
|
|
95
|
-
a(?x, n64000) :- a(?x, n63000).
|
|
96
|
-
a(?x, n65000) :- a(?x, n64000).
|
|
97
|
-
a(?x, n66000) :- a(?x, n65000).
|
|
98
|
-
a(?x, n67000) :- a(?x, n66000).
|
|
99
|
-
a(?x, n68000) :- a(?x, n67000).
|
|
100
|
-
a(?x, n69000) :- a(?x, n68000).
|
|
101
|
-
a(?x, n70000) :- a(?x, n69000).
|
|
102
|
-
a(?x, n71000) :- a(?x, n70000).
|
|
103
|
-
a(?x, n72000) :- a(?x, n71000).
|
|
104
|
-
a(?x, n73000) :- a(?x, n72000).
|
|
105
|
-
a(?x, n74000) :- a(?x, n73000).
|
|
106
|
-
a(?x, n75000) :- a(?x, n74000).
|
|
107
|
-
a(?x, n76000) :- a(?x, n75000).
|
|
108
|
-
a(?x, n77000) :- a(?x, n76000).
|
|
109
|
-
a(?x, n78000) :- a(?x, n77000).
|
|
110
|
-
a(?x, n79000) :- a(?x, n78000).
|
|
111
|
-
a(?x, n80000) :- a(?x, n79000).
|
|
112
|
-
a(?x, n81000) :- a(?x, n80000).
|
|
113
|
-
a(?x, n82000) :- a(?x, n81000).
|
|
114
|
-
a(?x, n83000) :- a(?x, n82000).
|
|
115
|
-
a(?x, n84000) :- a(?x, n83000).
|
|
116
|
-
a(?x, n85000) :- a(?x, n84000).
|
|
117
|
-
a(?x, n86000) :- a(?x, n85000).
|
|
118
|
-
a(?x, n87000) :- a(?x, n86000).
|
|
119
|
-
a(?x, n88000) :- a(?x, n87000).
|
|
120
|
-
a(?x, n89000) :- a(?x, n88000).
|
|
121
|
-
a(?x, n90000) :- a(?x, n89000).
|
|
122
|
-
a(?x, n91000) :- a(?x, n90000).
|
|
123
|
-
a(?x, n92000) :- a(?x, n91000).
|
|
124
|
-
a(?x, n93000) :- a(?x, n92000).
|
|
125
|
-
a(?x, n94000) :- a(?x, n93000).
|
|
126
|
-
a(?x, n95000) :- a(?x, n94000).
|
|
127
|
-
a(?x, n96000) :- a(?x, n95000).
|
|
128
|
-
a(?x, n97000) :- a(?x, n96000).
|
|
129
|
-
a(?x, n98000) :- a(?x, n97000).
|
|
130
|
-
a(?x, n99000) :- a(?x, n98000).
|
|
131
|
-
a(?x, n100000) :- a(?x, n99000).
|
|
132
|
-
a(?x, n49999) :- a(?x, n49000).
|
|
133
|
-
a(?x, n99999) :- a(?x, n99000).
|
|
134
|
-
|
|
135
27
|
% Adjacent N3-style taxonomy rules.
|
|
136
28
|
|
|
137
29
|
a(?x, n1) :- a(?x, n0).
|
|
@@ -300167,7 +300059,7 @@ arc(check6, "C6 OK - the success flag is raised only after the terminal class a2
|
|
|
300167
300059
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100000 and then as a2.") :-
|
|
300168
300060
|
once(is(test, true)).
|
|
300169
300061
|
|
|
300170
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain
|
|
300062
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain: each rule advances one taxonomy level and adds the matching side labels.") :-
|
|
300171
300063
|
once(a(ind, a2)),
|
|
300172
300064
|
once(is(test, true)).
|
|
300173
300065
|
|
|
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n9 to n10 was completed.").
|
|
|
6
6
|
arc(check5, "C5 OK - once n10 is reached, the terminal class a2 is derived.").
|
|
7
7
|
arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
|
|
8
8
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10 and then as a2.").
|
|
9
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain
|
|
9
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10 chain: each rule advances one taxonomy level and adds the matching side labels.").
|
|
10
10
|
checkPassed(report, check1).
|
|
11
11
|
checkPassed(report, check2).
|
|
12
12
|
checkPassed(report, check3).
|
|
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n99 to n100 was completed.").
|
|
|
6
6
|
arc(check5, "C5 OK - once n100 is reached, the terminal class a2 is derived.").
|
|
7
7
|
arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
|
|
8
8
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100 and then as a2.").
|
|
9
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain
|
|
9
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100 chain: each rule advances one taxonomy level and adds the matching side labels.").
|
|
10
10
|
checkPassed(report, check1).
|
|
11
11
|
checkPassed(report, check2).
|
|
12
12
|
checkPassed(report, check3).
|
|
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n999 to n1000 was completed.")
|
|
|
6
6
|
arc(check5, "C5 OK - once n1000 is reached, the terminal class a2 is derived.").
|
|
7
7
|
arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
|
|
8
8
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n1000 and then as a2.").
|
|
9
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain
|
|
9
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-1000 chain: each rule advances one taxonomy level and adds the matching side labels.").
|
|
10
10
|
checkPassed(report, check1).
|
|
11
11
|
checkPassed(report, check2).
|
|
12
12
|
checkPassed(report, check3).
|
|
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n9999 to n10000 was completed.
|
|
|
6
6
|
arc(check5, "C5 OK - once n10000 is reached, the terminal class a2 is derived.").
|
|
7
7
|
arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
|
|
8
8
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n10000 and then as a2.").
|
|
9
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain
|
|
9
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-10000 chain: each rule advances one taxonomy level and adds the matching side labels.").
|
|
10
10
|
checkPassed(report, check1).
|
|
11
11
|
checkPassed(report, check2).
|
|
12
12
|
checkPassed(report, check3).
|
|
@@ -6,7 +6,7 @@ arc(check4, "C4 OK - the final taxonomy step from n99999 to n100000 was complete
|
|
|
6
6
|
arc(check5, "C5 OK - once n100000 is reached, the terminal class a2 is derived.").
|
|
7
7
|
arc(check6, "C6 OK - the success flag is raised only after the terminal class a2 is present.").
|
|
8
8
|
answer(report, "The test succeeds: starting from one individual classified as n0, the rules eventually classify it as n100000 and then as a2.").
|
|
9
|
-
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain
|
|
9
|
+
reason(report, "The adjacent rules mirror the Eyeling N3 deep-taxonomy-100000 chain: each rule advances one taxonomy level and adds the matching side labels.").
|
|
10
10
|
checkPassed(report, check1).
|
|
11
11
|
checkPassed(report, check2).
|
|
12
12
|
checkPassed(report, check3).
|
package/package.json
CHANGED
package/src/builtins/control.js
CHANGED
|
@@ -11,15 +11,19 @@ function* notBuiltin({ solver, goal, env }) {
|
|
|
11
11
|
const limited = solver.cloneForInnerGoal(1);
|
|
12
12
|
let found = false;
|
|
13
13
|
for (const _ of limited.solve([goal.args[0]], env.clone(), 0)) { found = true; break; }
|
|
14
|
+
solver.absorbStatsFrom(limited);
|
|
14
15
|
if (!found) yield env;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
function* onceBuiltin({ solver, goal, env }) {
|
|
18
19
|
const limited = solver.cloneForInnerGoal(1);
|
|
20
|
+
let first = null;
|
|
19
21
|
for (const answerEnv of limited.solve([goal.args[0]], env.clone(), 0)) {
|
|
20
|
-
|
|
22
|
+
first = answerEnv;
|
|
21
23
|
break;
|
|
22
24
|
}
|
|
25
|
+
solver.absorbStatsFrom(limited);
|
|
26
|
+
if (first) yield first;
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
function* forallBuiltin({ solver, goal, env }) {
|
|
@@ -28,7 +32,12 @@ function* forallBuiltin({ solver, goal, env }) {
|
|
|
28
32
|
const checker = solver.cloneForInnerGoal(1);
|
|
29
33
|
let ok = false;
|
|
30
34
|
for (const _ of checker.solve([goal.args[1]], answerEnv.clone(), 0)) { ok = true; break; }
|
|
31
|
-
|
|
35
|
+
solver.absorbStatsFrom(checker);
|
|
36
|
+
if (!ok) {
|
|
37
|
+
solver.absorbStatsFrom(generator);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
32
40
|
}
|
|
41
|
+
solver.absorbStatsFrom(generator);
|
|
33
42
|
yield env;
|
|
34
43
|
}
|
package/src/cli.js
CHANGED
|
@@ -103,13 +103,12 @@ async function runDefault(engine, program, options) {
|
|
|
103
103
|
const materializedKeys = new Set(goals.map((goal) => `${goal.name}/${goal.arity}`));
|
|
104
104
|
const facts = program.sourceFactLines(materializedKeys);
|
|
105
105
|
const lines = new Set();
|
|
106
|
-
let lastStats = null;
|
|
107
106
|
const registry = engine.getDefaultRegistry();
|
|
108
107
|
const explanation = options.proof ? await loadExplanation() : null;
|
|
108
|
+
const solver = new engine.Solver(program, { registry });
|
|
109
109
|
|
|
110
110
|
for (const goal of goals) {
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
solver.solutionsSeen = 0;
|
|
113
112
|
for (const env of solver.solve([goal], new engine.Env(), 0)) {
|
|
114
113
|
if (!engine.termIsGround(goal, env)) continue;
|
|
115
114
|
|
|
@@ -122,10 +121,9 @@ async function runDefault(engine, program, options) {
|
|
|
122
121
|
if (options.proof) writeExplanation(explanation, program, engine.copyResolved(goal, env), registry);
|
|
123
122
|
}
|
|
124
123
|
|
|
125
|
-
lastStats = solver.stats;
|
|
126
124
|
}
|
|
127
125
|
|
|
128
|
-
if (options.stats
|
|
126
|
+
if (options.stats) printStats(solver.stats);
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
function writeExplanation(explanation, program, resolved, registry) {
|
package/src/solver.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Depth-first eyelang solver with builtin dispatch, memoization, and guarded recursion handling.
|
|
2
2
|
// Most semantic decisions still flow through unification; optimizations only select candidates earlier.
|
|
3
|
-
import { COMPOUND, Env, flattenConjunction, freshTerm, termToString, unify, variantTerms } from './term.js';
|
|
3
|
+
import { COMPOUND, Env, copyResolved, flattenConjunction, freshTerm, termIsGround, termToString, unify, variantTerms } from './term.js';
|
|
4
4
|
import { createDefaultRegistry } from './builtins/registry.js';
|
|
5
5
|
import { selectClauseCandidates } from './program.js';
|
|
6
6
|
|
|
@@ -19,6 +19,7 @@ export class Solver {
|
|
|
19
19
|
this.solutionsSeen = 0;
|
|
20
20
|
this.active = [];
|
|
21
21
|
this.memo = new Map();
|
|
22
|
+
this.groundChainSuccess = new Set();
|
|
22
23
|
this.stats = {
|
|
23
24
|
completed_goal_lists: 0,
|
|
24
25
|
solve_goals_calls: 0,
|
|
@@ -34,9 +35,21 @@ export class Solver {
|
|
|
34
35
|
cloneForInnerGoal(solutionLimit = this.solutionLimit) {
|
|
35
36
|
const solver = new Solver(this.program, { registry: this.registry, maxDepth: this.maxDepth, solutionLimit });
|
|
36
37
|
solver.memo = this.memo;
|
|
38
|
+
solver.groundChainSuccess = this.groundChainSuccess;
|
|
37
39
|
return solver;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
absorbStatsFrom(child) {
|
|
43
|
+
if (!child || child === this || !child.stats) return;
|
|
44
|
+
for (const [key, value] of Object.entries(child.stats)) {
|
|
45
|
+
if (key === 'max_depth' || key === 'max_goal_count') {
|
|
46
|
+
this.stats[key] = Math.max(this.stats[key] ?? 0, value ?? 0);
|
|
47
|
+
} else {
|
|
48
|
+
this.stats[key] = (this.stats[key] ?? 0) + (value ?? 0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
*solve(goals, env = new Env(), depth = 0) {
|
|
41
54
|
if (!Array.isArray(goals)) goals = [goals];
|
|
42
55
|
|
|
@@ -267,6 +280,7 @@ function pushMemoAnswerFrames(stack, entry, goal, rest, env, depth, active, solv
|
|
|
267
280
|
|
|
268
281
|
function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth, active) {
|
|
269
282
|
if (activeVariantIn(goal, env, active)) return;
|
|
283
|
+
if (tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active)) return;
|
|
270
284
|
const candidates = selectClauseCandidates(group, goal, env);
|
|
271
285
|
const frames = [];
|
|
272
286
|
for (const pass of [candidates.primary, candidates.fallback]) {
|
|
@@ -294,6 +308,80 @@ function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth
|
|
|
294
308
|
for (let i = frames.length - 1; i >= 0; i--) stack.push(frames[i]);
|
|
295
309
|
}
|
|
296
310
|
|
|
311
|
+
|
|
312
|
+
function tryPushGroundChainFrames(stack, solver, group, goal, rest, env, depth, active) {
|
|
313
|
+
// Compress deterministic ground single-goal chains such as deep taxonomy
|
|
314
|
+
// proofs: a(ind, n100000) -> a(ind, n99999) -> ... -> a(ind, n0).
|
|
315
|
+
// This is a search-control optimization only. It fires only while each step
|
|
316
|
+
// has exactly one matching clause and a single ground body goal; otherwise the
|
|
317
|
+
// normal clause path below remains authoritative.
|
|
318
|
+
if (!termIsGround(goal, env)) return false;
|
|
319
|
+
|
|
320
|
+
const baseEnv = env;
|
|
321
|
+
let currentGroup = group;
|
|
322
|
+
let currentGoal = copyResolved(goal, env);
|
|
323
|
+
let currentDepth = depth;
|
|
324
|
+
const currentEnv = new Env();
|
|
325
|
+
const seen = new Set();
|
|
326
|
+
|
|
327
|
+
while (true) {
|
|
328
|
+
// The compressed path is iterative and protected by `seen`, so it does not
|
|
329
|
+
// consume JavaScript recursion depth the way the ordinary solver path does.
|
|
330
|
+
// Keep recording the logical depth for diagnostics, but do not cut off long
|
|
331
|
+
// finite taxonomy chains with the recursive maxDepth guard.
|
|
332
|
+
if (solver.solutionsSeen >= solver.solutionLimit) return true;
|
|
333
|
+
solver.stats.max_depth = Math.max(solver.stats.max_depth, currentDepth);
|
|
334
|
+
const key = `${currentGoal.name}/${currentGoal.arity}:${termToString(currentGoal, currentEnv, true)}`;
|
|
335
|
+
if (seen.has(key)) return true;
|
|
336
|
+
if (activeVariantIn(currentGoal, currentEnv, active)) return true;
|
|
337
|
+
if (solver.groundChainSuccess.has(key)) {
|
|
338
|
+
rememberGroundChainSuccess(solver, seen);
|
|
339
|
+
stack.push({ kind: 'goals', goals: rest, env: baseEnv, depth: depth + 1, active });
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
seen.add(key);
|
|
343
|
+
|
|
344
|
+
const candidates = selectClauseCandidates(currentGroup, currentGoal, currentEnv);
|
|
345
|
+
const matches = [];
|
|
346
|
+
for (const pass of [candidates.primary, candidates.fallback]) {
|
|
347
|
+
for (const clause of pass) {
|
|
348
|
+
if (headCannotMatch(currentGoal, clause.head, currentEnv)) continue;
|
|
349
|
+
const id = nextFreshId();
|
|
350
|
+
const freshHead = freshTerm(clause.head, id);
|
|
351
|
+
const freshBody = clause.body.map((term) => freshTerm(term, id));
|
|
352
|
+
const next = new Env();
|
|
353
|
+
solver.stats.unify_calls++;
|
|
354
|
+
if (!unify(currentGoal, freshHead, next)) continue;
|
|
355
|
+
matches.push({ body: freshBody, env: next });
|
|
356
|
+
if (matches.length > 1) return false;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (matches.length !== 1) return false;
|
|
361
|
+
const match = matches[0];
|
|
362
|
+
if (match.body.length === 0) {
|
|
363
|
+
rememberGroundChainSuccess(solver, seen);
|
|
364
|
+
stack.push({ kind: 'goals', goals: rest, env: baseEnv, depth: depth + 1, active });
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
if (match.body.length !== 1) return false;
|
|
368
|
+
const nextGoal = match.body[0];
|
|
369
|
+
if (nextGoal.type !== COMPOUND || !termIsGround(nextGoal, match.env)) return false;
|
|
370
|
+
const resolvedNextGoal = copyResolved(nextGoal, match.env);
|
|
371
|
+
const nextGroup = solver.program.findGroup(resolvedNextGoal.name, resolvedNextGoal.arity);
|
|
372
|
+
if (!nextGroup) return false;
|
|
373
|
+
|
|
374
|
+
currentGoal = resolvedNextGoal;
|
|
375
|
+
currentGroup = nextGroup;
|
|
376
|
+
currentDepth++;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
function rememberGroundChainSuccess(solver, seen) {
|
|
382
|
+
for (const key of seen) solver.groundChainSuccess.add(key);
|
|
383
|
+
}
|
|
384
|
+
|
|
297
385
|
function rememberMemoAnswer(entry, goal, env) {
|
|
298
386
|
const answerArgs = goal.args.map((arg) => importResolved(arg, env));
|
|
299
387
|
const key = answerArgs.map((arg) => termToString(arg, new Env(), true)).join('\x1f');
|