eyelang 1.7.11 → 1.7.13
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 -1
- package/docs/guide.md +2 -1
- package/examples/markov-logic-network.eye +87 -0
- package/examples/output/markov-logic-network.eye +32 -0
- package/examples/stirling-bell-numbers.eye +50 -13
- package/package.json +2 -2
- package/playground.html +1 -0
- package/test/run-regression.mjs +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/eyelang)
|
|
4
4
|
[](https://doi.org/10.5281/zenodo.20761726)
|
|
5
5
|
|
|
6
|
-
Eyelang is a small logic programming language for rules, goals, answers, and proofs.
|
|
6
|
+
Eyelang is a small logic programming language for facts, rules, goals, answers, and proofs.
|
|
7
7
|
Its source syntax is Prolog-like Horn-clause syntax with deliberate eyelang choices, including `?x` variables for N3/SPARQL-style readability, explicit `table(path, 2).` declarations for tabled predicates, advisory `mode/3` declarations for host tooling, explicit Herbrand witness terms for executable existential-style consequences, and stratified-negation diagnostics for portable `not/1` usage.
|
|
8
8
|
It grew out of logic-language experiments in the EYE/N3 reasoning tradition, but is packaged here as its own project.
|
|
9
9
|
|
package/docs/guide.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Eyelang Guide
|
|
2
2
|
|
|
3
|
-
This guide introduces Eyelang, a small Horn-clause language and engine whose source syntax is Prolog-like but deliberately its own compact language for rules, goals, answers, and proofs. Eyelang works over ordinary terms, lists, arithmetic, strings, and finite search. Run it with the `eyelang` CLI, or use `node bin/eyelang.js` when working directly from a source checkout.
|
|
3
|
+
This guide introduces Eyelang, a small Horn-clause language and engine whose source syntax is Prolog-like but deliberately its own compact language for facts, rules, goals, answers, and proofs. Eyelang works over ordinary terms, lists, arithmetic, strings, and finite search. Run it with the `eyelang` CLI, or use `node bin/eyelang.js` when working directly from a source checkout.
|
|
4
4
|
|
|
5
5
|
Programs write relations directly, for example `ancestor(pat, emma)` or `status(case1, accepted)`. Absolute IRI atoms can be written explicitly with angle brackets, for example `<https://schema.org/name>`, when a program needs web identifiers without prefix declarations. Eyelang output is ordinary Eyelang syntax: by default, the CLI materializes selected answer facts and prints those facts only. Pass `--proof` (or `-p`) when you also want each answer followed by a `why/2` explanation fact that records the proof. Programs may add `materialize/2` declarations such as `materialize(answer, 2).` to focus output on selected predicates.
|
|
6
6
|
|
|
@@ -414,6 +414,7 @@ Use `holds/2` when you want to match the member term directly, for example `name
|
|
|
414
414
|
| [`list-collection.eye`](../examples/list-collection.eye) | Demonstrates list and collection built-ins. | [`output/list-collection.eye`](../examples/output/list-collection.eye) |
|
|
415
415
|
| [`lldm.eye`](../examples/lldm.eye) | Calculates leg-length discrepancy measurements. | [`output/lldm.eye`](../examples/output/lldm.eye) |
|
|
416
416
|
| [`manufacturing-quality-control.eye`](../examples/manufacturing-quality-control.eye) | Evaluates process capability and quality. | [`output/manufacturing-quality-control.eye`](../examples/output/manufacturing-quality-control.eye) |
|
|
417
|
+
| [`markov-logic-network.eye`](../examples/markov-logic-network.eye) | Scores finite possible worlds with weighted soft formulas in a Markov Logic Network style. | [`output/markov-logic-network.eye`](../examples/output/markov-logic-network.eye) |
|
|
417
418
|
| [`map-four-color-search.eye`](../examples/map-four-color-search.eye) | Searches for a valid four-colouring of the EU neighbour graph. | [`output/map-four-color-search.eye`](../examples/output/map-four-color-search.eye) |
|
|
418
419
|
| [`matrix-chain-order.eye`](../examples/matrix-chain-order.eye) | Finds an optimal matrix-chain multiplication order. | [`output/matrix-chain-order.eye`](../examples/output/matrix-chain-order.eye) |
|
|
419
420
|
| [`matrix-noncommutativity.eye`](../examples/matrix-noncommutativity.eye) | Multiplies 2x2 matrices and shows non-commutativity. | [`output/matrix-noncommutativity.eye`](../examples/output/matrix-noncommutativity.eye) |
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
% Markov Logic Network style scoring over a tiny finite domain.
|
|
2
|
+
%
|
|
3
|
+
% Eyelang is deterministic, so this is not a probabilistic MLN engine. The
|
|
4
|
+
% example encodes the usual MLN MAP idea explicitly: enumerate possible worlds,
|
|
5
|
+
% mark which weighted soft formulas each world satisfies, sum the weights, and
|
|
6
|
+
% choose the highest-scoring world. Weights are stored as integer tenths of a
|
|
7
|
+
% log weight so the example stays reproducible without floating-point noise.
|
|
8
|
+
|
|
9
|
+
materialize(mlnWeight, 2).
|
|
10
|
+
materialize(mlnWorld, 2).
|
|
11
|
+
materialize(mlnSatisfied, 2).
|
|
12
|
+
materialize(mlnViolated, 2).
|
|
13
|
+
materialize(mlnContribution, 3).
|
|
14
|
+
materialize(mlnWorldScore, 2).
|
|
15
|
+
materialize(mlnMapWorld, 2).
|
|
16
|
+
materialize(mlnConclusion, 2).
|
|
17
|
+
|
|
18
|
+
% Evidence and candidate hidden assignments.
|
|
19
|
+
person(alice).
|
|
20
|
+
person(bob).
|
|
21
|
+
friend(alice, bob).
|
|
22
|
+
observed_smokes(alice).
|
|
23
|
+
|
|
24
|
+
candidate_world(w_bob_not_smokes_not_cancer, no, no).
|
|
25
|
+
candidate_world(w_bob_not_smokes_cancer, no, yes).
|
|
26
|
+
candidate_world(w_bob_smokes_not_cancer, yes, no).
|
|
27
|
+
candidate_world(w_bob_smokes_cancer, yes, yes).
|
|
28
|
+
|
|
29
|
+
% Soft formulas are weighted log features.
|
|
30
|
+
formula_weight_tenths(friend_smoking, 20).
|
|
31
|
+
formula_weight_tenths(smoking_causes_cancer, 13).
|
|
32
|
+
formula_weight_tenths(cancer_is_rare, 6).
|
|
33
|
+
|
|
34
|
+
% World interpretation for Bob. Alice's smoking status is observed evidence.
|
|
35
|
+
smokes_in_world(?world, alice) :-
|
|
36
|
+
candidate_world(?world, ?, ?),
|
|
37
|
+
observed_smokes(alice).
|
|
38
|
+
smokes_in_world(?world, bob) :- candidate_world(?world, yes, ?).
|
|
39
|
+
cancer_in_world(?world, bob) :- candidate_world(?world, ?, yes).
|
|
40
|
+
|
|
41
|
+
% Grounded soft formulas for this tiny domain:
|
|
42
|
+
% friend_smoking: friend(alice,bob) and smokes(alice) => smokes(bob)
|
|
43
|
+
% smoking_causes_cancer: smokes(bob) => cancer(bob)
|
|
44
|
+
% cancer_is_rare: not cancer(bob)
|
|
45
|
+
formula_satisfied(?world, friend_smoking) :-
|
|
46
|
+
friend(alice, bob),
|
|
47
|
+
smokes_in_world(?world, alice),
|
|
48
|
+
smokes_in_world(?world, bob).
|
|
49
|
+
|
|
50
|
+
formula_satisfied(?world, smoking_causes_cancer) :-
|
|
51
|
+
candidate_world(?world, no, ?).
|
|
52
|
+
formula_satisfied(?world, smoking_causes_cancer) :-
|
|
53
|
+
smokes_in_world(?world, bob),
|
|
54
|
+
cancer_in_world(?world, bob).
|
|
55
|
+
|
|
56
|
+
formula_satisfied(?world, cancer_is_rare) :-
|
|
57
|
+
candidate_world(?world, ?, no).
|
|
58
|
+
|
|
59
|
+
formula_violated(?world, ?formula) :-
|
|
60
|
+
candidate_world(?world, ?, ?),
|
|
61
|
+
formula_weight_tenths(?formula, ?),
|
|
62
|
+
not(formula_satisfied(?world, ?formula)).
|
|
63
|
+
|
|
64
|
+
contribution_tenths(?world, ?formula, ?weight) :-
|
|
65
|
+
formula_satisfied(?world, ?formula),
|
|
66
|
+
formula_weight_tenths(?formula, ?weight).
|
|
67
|
+
|
|
68
|
+
world_score_tenths(?world, ?score) :-
|
|
69
|
+
candidate_world(?world, ?, ?),
|
|
70
|
+
sumall(?weight, contribution_tenths(?world, ?formula, ?weight), ?score).
|
|
71
|
+
|
|
72
|
+
map_world(?world, ?score) :-
|
|
73
|
+
aggregate_max(?candidate_score, ?candidate_world,
|
|
74
|
+
world_score_tenths(?candidate_world, ?candidate_score),
|
|
75
|
+
?score, ?world).
|
|
76
|
+
|
|
77
|
+
mlnWeight(?formula, log_weight_tenths(?weight)) :- formula_weight_tenths(?formula, ?weight).
|
|
78
|
+
mlnWorld(?world, world(smokes(bob, ?smokes), cancer(bob, ?cancer))) :-
|
|
79
|
+
candidate_world(?world, ?smokes, ?cancer).
|
|
80
|
+
mlnSatisfied(?world, ?formula) :- formula_satisfied(?world, ?formula).
|
|
81
|
+
mlnViolated(?world, ?formula) :- formula_violated(?world, ?formula).
|
|
82
|
+
mlnContribution(?world, ?formula, log_weight_tenths(?weight)) :-
|
|
83
|
+
contribution_tenths(?world, ?formula, ?weight).
|
|
84
|
+
mlnWorldScore(?world, log_weight_tenths(?score)) :- world_score_tenths(?world, ?score).
|
|
85
|
+
mlnMapWorld(?world, log_weight_tenths(?score)) :- map_world(?world, ?score).
|
|
86
|
+
mlnConclusion(case, "MAP world predicts that Bob smokes and has cancer") :-
|
|
87
|
+
map_world(w_bob_smokes_cancer, ?).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
mlnWeight(friend_smoking, log_weight_tenths(20)).
|
|
2
|
+
mlnWeight(smoking_causes_cancer, log_weight_tenths(13)).
|
|
3
|
+
mlnWeight(cancer_is_rare, log_weight_tenths(6)).
|
|
4
|
+
mlnWorld(w_bob_not_smokes_not_cancer, world(smokes(bob, no), cancer(bob, no))).
|
|
5
|
+
mlnWorld(w_bob_not_smokes_cancer, world(smokes(bob, no), cancer(bob, yes))).
|
|
6
|
+
mlnWorld(w_bob_smokes_not_cancer, world(smokes(bob, yes), cancer(bob, no))).
|
|
7
|
+
mlnWorld(w_bob_smokes_cancer, world(smokes(bob, yes), cancer(bob, yes))).
|
|
8
|
+
mlnSatisfied(w_bob_smokes_not_cancer, friend_smoking).
|
|
9
|
+
mlnSatisfied(w_bob_smokes_cancer, friend_smoking).
|
|
10
|
+
mlnSatisfied(w_bob_not_smokes_not_cancer, smoking_causes_cancer).
|
|
11
|
+
mlnSatisfied(w_bob_not_smokes_cancer, smoking_causes_cancer).
|
|
12
|
+
mlnSatisfied(w_bob_smokes_cancer, smoking_causes_cancer).
|
|
13
|
+
mlnSatisfied(w_bob_not_smokes_not_cancer, cancer_is_rare).
|
|
14
|
+
mlnSatisfied(w_bob_smokes_not_cancer, cancer_is_rare).
|
|
15
|
+
mlnViolated(w_bob_not_smokes_not_cancer, friend_smoking).
|
|
16
|
+
mlnViolated(w_bob_not_smokes_cancer, friend_smoking).
|
|
17
|
+
mlnViolated(w_bob_not_smokes_cancer, cancer_is_rare).
|
|
18
|
+
mlnViolated(w_bob_smokes_not_cancer, smoking_causes_cancer).
|
|
19
|
+
mlnViolated(w_bob_smokes_cancer, cancer_is_rare).
|
|
20
|
+
mlnContribution(w_bob_smokes_not_cancer, friend_smoking, log_weight_tenths(20)).
|
|
21
|
+
mlnContribution(w_bob_smokes_cancer, friend_smoking, log_weight_tenths(20)).
|
|
22
|
+
mlnContribution(w_bob_not_smokes_not_cancer, smoking_causes_cancer, log_weight_tenths(13)).
|
|
23
|
+
mlnContribution(w_bob_not_smokes_cancer, smoking_causes_cancer, log_weight_tenths(13)).
|
|
24
|
+
mlnContribution(w_bob_smokes_cancer, smoking_causes_cancer, log_weight_tenths(13)).
|
|
25
|
+
mlnContribution(w_bob_not_smokes_not_cancer, cancer_is_rare, log_weight_tenths(6)).
|
|
26
|
+
mlnContribution(w_bob_smokes_not_cancer, cancer_is_rare, log_weight_tenths(6)).
|
|
27
|
+
mlnWorldScore(w_bob_not_smokes_not_cancer, log_weight_tenths(19)).
|
|
28
|
+
mlnWorldScore(w_bob_not_smokes_cancer, log_weight_tenths(13)).
|
|
29
|
+
mlnWorldScore(w_bob_smokes_not_cancer, log_weight_tenths(26)).
|
|
30
|
+
mlnWorldScore(w_bob_smokes_cancer, log_weight_tenths(33)).
|
|
31
|
+
mlnMapWorld(w_bob_smokes_cancer, log_weight_tenths(33)).
|
|
32
|
+
mlnConclusion(case, "MAP world predicts that Bob smokes and has cancer").
|
|
@@ -1,30 +1,67 @@
|
|
|
1
1
|
% Stirling numbers of the second kind and Bell numbers.
|
|
2
2
|
%
|
|
3
|
-
%
|
|
4
|
-
%
|
|
5
|
-
%
|
|
6
|
-
%
|
|
3
|
+
% The Stirling count S(N,K) is computed with the inclusion-exclusion formula
|
|
4
|
+
% S(N,K) = (1/K!) * sum(I=0..K, (-1)^(K-I) * C(K,I) * I^N)
|
|
5
|
+
% instead of the overlapping two-branch recurrence. Bell numbers use
|
|
6
|
+
% B(0) = 1, B(N) = sum(K=0..N-1, C(N-1,K) * B(K)).
|
|
7
|
+
% The table declarations memoize the smaller helper relations used by both formulas.
|
|
7
8
|
materialize(stirling_bell_answer, 2).
|
|
8
9
|
|
|
10
|
+
table(factorial, 2).
|
|
11
|
+
table(binomial, 3).
|
|
9
12
|
table(stirling2, 3).
|
|
13
|
+
table(bell, 2).
|
|
14
|
+
|
|
15
|
+
factorial(0, 1).
|
|
16
|
+
factorial(?n, ?value) :-
|
|
17
|
+
gt(?n, 0),
|
|
18
|
+
sub(?n, 1, ?n1),
|
|
19
|
+
factorial(?n1, ?previous),
|
|
20
|
+
mul(?n, ?previous, ?value).
|
|
21
|
+
|
|
22
|
+
binomial(0, 0, 1).
|
|
23
|
+
binomial(?n, 0, 1) :- gt(?n, 0).
|
|
24
|
+
binomial(?n, ?n, 1) :- gt(?n, 0).
|
|
25
|
+
binomial(?n, ?k, ?value) :-
|
|
26
|
+
gt(?n, 0),
|
|
27
|
+
gt(?k, 0),
|
|
28
|
+
lt(?k, ?n),
|
|
29
|
+
sub(?n, 1, ?n1),
|
|
30
|
+
sub(?k, 1, ?k1),
|
|
31
|
+
binomial(?n1, ?k1, ?left),
|
|
32
|
+
binomial(?n1, ?k, ?right),
|
|
33
|
+
add(?left, ?right, ?value).
|
|
34
|
+
|
|
35
|
+
signed_term(?n, ?k, ?i, ?term) :-
|
|
36
|
+
binomial(?k, ?i, ?c),
|
|
37
|
+
pow(?i, ?n, ?p),
|
|
38
|
+
mul(?c, ?p, ?unsigned),
|
|
39
|
+
sub(?k, ?i, ?d),
|
|
40
|
+
mod(?d, 2, 0),
|
|
41
|
+
eq(?term, ?unsigned).
|
|
42
|
+
signed_term(?n, ?k, ?i, ?term) :-
|
|
43
|
+
binomial(?k, ?i, ?c),
|
|
44
|
+
pow(?i, ?n, ?p),
|
|
45
|
+
mul(?c, ?p, ?unsigned),
|
|
46
|
+
sub(?k, ?i, ?d),
|
|
47
|
+
mod(?d, 2, 1),
|
|
48
|
+
neg(?unsigned, ?term).
|
|
10
49
|
|
|
11
|
-
% Boundary cases define the empty partition and the impossible zero-block columns.
|
|
12
50
|
stirling2(0, 0, 1).
|
|
13
51
|
stirling2(?n, 0, 0) :- gt(?n, 0).
|
|
14
52
|
stirling2(0, ?k, 0) :- gt(?k, 0).
|
|
15
53
|
stirling2(?n, ?k, ?count) :-
|
|
16
54
|
gt(?n, 0),
|
|
17
55
|
gt(?k, 0),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
stirling2(?n1, ?k, ?existingblocks),
|
|
22
|
-
mul(?k, ?existingblocks, ?extended),
|
|
23
|
-
add(?newblock, ?extended, ?count).
|
|
56
|
+
sumall(?term, (between(0, ?k, ?i), signed_term(?n, ?k, ?i, ?term)), ?sum),
|
|
57
|
+
factorial(?k, ?factorial),
|
|
58
|
+
div(?sum, ?factorial, ?count).
|
|
24
59
|
|
|
25
|
-
|
|
60
|
+
bell(0, 1).
|
|
26
61
|
bell(?n, ?count) :-
|
|
27
|
-
|
|
62
|
+
gt(?n, 0),
|
|
63
|
+
sub(?n, 1, ?n1),
|
|
64
|
+
sumall(?term, (between(0, ?n1, ?k), binomial(?n1, ?k, ?choose), bell(?k, ?bell), mul(?choose, ?bell, ?term)), ?count).
|
|
28
65
|
|
|
29
66
|
stirling_bell_answer(stirling_10_4, ?count) :- stirling2(10, 4, ?count).
|
|
30
67
|
stirling_bell_answer(stirling_12_5, ?count) :- stirling2(12, 5, ?count).
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyelang",
|
|
3
|
-
"version": "1.7.
|
|
4
|
-
"description": "A small Prolog-like logic programming language for rules, goals, answers, and proofs.",
|
|
3
|
+
"version": "1.7.13",
|
|
4
|
+
"description": "A small Prolog-like logic programming language for facts, rules, goals, answers, and proofs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"types": "./index.d.ts",
|
package/playground.html
CHANGED
package/test/run-regression.mjs
CHANGED
|
@@ -800,7 +800,7 @@ function whiteBoxCases() {
|
|
|
800
800
|
['matrix-chain-order.eye', 'cost', 3, false],
|
|
801
801
|
['modular-exponentiation.eye', 'pow_mod', 4, true],
|
|
802
802
|
['pell-equation.eye', 'pell', 3, true],
|
|
803
|
-
['stirling-bell-numbers.eye', 'stirling2', 3,
|
|
803
|
+
['stirling-bell-numbers.eye', 'stirling2', 3, false],
|
|
804
804
|
['totient-summatory.eye', 'gcd', 3, true],
|
|
805
805
|
['totient-summatory.eye', 'totient', 2, false],
|
|
806
806
|
['weighted-interval-scheduling.eye', 'best_from', 2, true],
|