eyeprolog 1.5.105 → 1.5.107

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.
@@ -5,14 +5,19 @@
5
5
  % distinctness, and delayed diagonal constraints describe the puzzle before
6
6
  % labeling searches the remaining finite alternatives.
7
7
  %
8
- % The checked eight-queens goal verifies a known witness with the same relational
9
- % model. A smaller four-queens query exercises actual search and multiple-solution
10
- % enumeration without making the default example suite a CLP(Z) benchmark.
8
+ % queens8_witness/1 does NOT search: it unifies Rows with a known witness
9
+ % FIRST and only then calls queens/2, so every position is already ground
10
+ % before labeling/2 ever runs. It is a fast constraint check against a
11
+ % known-good answer, not a demonstration of solving the puzzle -- the name
12
+ % says so, so it cannot be mistaken for one. A smaller four-queens query
13
+ % below is the routine, honestly-searched default: it exercises actual
14
+ % search and multiple-solution enumeration, small enough to stay fast
15
+ % without making the default example suite a CLP(Z) benchmark.
11
16
 
12
- %% goal: queens8_solution(X0)
17
+ %% goal: queens8_witness(X0)
13
18
  %% goal: queens(4, X0)
14
19
 
15
- queens8_solution(Rows) :-
20
+ queens8_witness(Rows) :-
16
21
  Rows = [1, 5, 8, 6, 3, 7, 2, 4],
17
22
  queens(8, Rows).
18
23
 
@@ -2,13 +2,43 @@
2
2
 
3
3
  % AI Escargot is a well-known difficult 9-by-9 Sudoku. sudoku9/1 is the
4
4
  % declarative search relation: every row, column, and 3-by-3 block is
5
- % all-distinct and first-fail labeling searches the remaining cells. The default
6
- % golden goal verifies the known full-size solution so routine tests exercise
7
- % the complete model without turning the example suite into a search benchmark.
5
+ % all-distinct and first-fail labeling searches the remaining cells.
6
+ %
7
+ % sudoku9_witness/1 below does NOT search. It unifies Rows with the
8
+ % well-known published AI Escargot answer FIRST and only then calls
9
+ % sudoku9/1, so every cell is already ground before labeling/2 ever runs --
10
+ % it is a fast constraint check on a known-good grid, not a demonstration of
11
+ % solving the puzzle, and it must never be read as one. Actually searching
12
+ % sudoku9/1 from the blanks with the labeling strategy below does not finish
13
+ % within minutes, because library(clpz)'s all_distinct/1 propagation is not
14
+ % yet strong enough for a puzzle this constrained at 9-by-9 (tracked
15
+ % separately as a propagation-strength issue). Until that is improved,
16
+ % sudoku4_solution/1 is the routine,
17
+ % honestly-searched default: a 4-by-4 "Shidoku" (2-by-2 blocks, digits 1-4)
18
+ % built from the identical relational model, actually solved from its
19
+ % blanks by the same labeling call, so the example suite still exercises
20
+ % real search on every run. Readers who want to watch the real, currently
21
+ % very slow 9-by-9 search can still call sudoku9(Rows) directly with an
22
+ % unbound Rows.
8
23
 
9
- %% goal: sudoku9_solution(X0)
24
+ %% goal: sudoku4_solution(X0)
25
+ %% goal: sudoku9_witness(X0)
10
26
 
11
- sudoku9_solution(Rows) :-
27
+ sudoku4_solution(Rows) :-
28
+ Rows = [
29
+ [_, _, 3, _],
30
+ [_, 4, _, 2],
31
+ [2, 1, _, _],
32
+ [_, _, 2, _]
33
+ ],
34
+ sudoku4_rows(Rows),
35
+ sudoku4_transpose(Rows, Columns),
36
+ sudoku4_rows_distinct(Columns),
37
+ sudoku4_blocks(Rows),
38
+ sudoku4_flatten(Rows, Cells),
39
+ labeling([ff], Cells).
40
+
41
+ sudoku9_witness(Rows) :-
12
42
  sudoku9_known_solution(Rows),
13
43
  sudoku9(Rows).
14
44
 
@@ -80,3 +110,45 @@ sudoku9_flatten([Row|Rows], Cells) :-
80
110
 
81
111
  sudoku9_append([], Ys, Ys).
82
112
  sudoku9_append([X|Xs], Ys, [X|Zs]) :- sudoku9_append(Xs, Ys, Zs).
113
+
114
+ % sudoku4_*/N below mirror the sudoku9_*/N relations at 4-by-4 scale (2-by-2
115
+ % blocks instead of 3-by-3), so sudoku4_solution/1 exercises the identical
116
+ % relational model as sudoku9/1, just small enough to search instantly.
117
+
118
+ sudoku4_rows([]).
119
+ sudoku4_rows([Row|Rows]) :-
120
+ Row ins 1..4,
121
+ all_distinct(Row),
122
+ sudoku4_rows(Rows).
123
+
124
+ sudoku4_rows_distinct([]).
125
+ sudoku4_rows_distinct([Row|Rows]) :-
126
+ all_distinct(Row),
127
+ sudoku4_rows_distinct(Rows).
128
+
129
+ sudoku4_transpose([[]|_], []).
130
+ sudoku4_transpose(Rows, [Column|Columns]) :-
131
+ sudoku4_heads_tails(Rows, Column, Tails),
132
+ sudoku4_transpose(Tails, Columns).
133
+
134
+ sudoku4_heads_tails([], [], []).
135
+ sudoku4_heads_tails([[Head|Tail]|Rows], [Head|Heads], [Tail|Tails]) :-
136
+ sudoku4_heads_tails(Rows, Heads, Tails).
137
+
138
+ sudoku4_blocks([]).
139
+ sudoku4_blocks([A, B|Rows]) :-
140
+ sudoku4_block_row(A, B),
141
+ sudoku4_blocks(Rows).
142
+
143
+ sudoku4_block_row([], []).
144
+ sudoku4_block_row([A, B|As], [C, D|Bs]) :-
145
+ all_distinct([A, B, C, D]),
146
+ sudoku4_block_row(As, Bs).
147
+
148
+ sudoku4_flatten([], []).
149
+ sudoku4_flatten([Row|Rows], Cells) :-
150
+ sudoku4_append(Row, Rest, Cells),
151
+ sudoku4_flatten(Rows, Rest).
152
+
153
+ sudoku4_append([], Ys, Ys).
154
+ sudoku4_append([X|Xs], Ys, [X|Zs]) :- sudoku4_append(Xs, Ys, Zs).
@@ -1,3 +1,3 @@
1
- queens8_solution([1, 5, 8, 6, 3, 7, 2, 4]).
1
+ queens8_witness([1, 5, 8, 6, 3, 7, 2, 4]).
2
2
  queens(4, [2, 4, 1, 3]).
3
3
  queens(4, [3, 1, 4, 2]).
@@ -1 +1,2 @@
1
- sudoku9_solution([[1, 6, 2, 8, 5, 7, 4, 9, 3], [5, 3, 4, 1, 2, 9, 6, 7, 8], [7, 8, 9, 6, 4, 3, 5, 2, 1], [4, 7, 5, 3, 1, 2, 9, 8, 6], [9, 1, 3, 5, 8, 6, 7, 4, 2], [6, 2, 8, 7, 9, 4, 1, 3, 5], [3, 5, 6, 4, 7, 8, 2, 1, 9], [2, 4, 1, 9, 3, 5, 8, 6, 7], [8, 9, 7, 2, 6, 1, 3, 5, 4]]).
1
+ sudoku4_solution([[1, 2, 3, 4], [3, 4, 1, 2], [2, 1, 4, 3], [4, 3, 2, 1]]).
2
+ sudoku9_witness([[1, 6, 2, 8, 5, 7, 4, 9, 3], [5, 3, 4, 1, 2, 9, 6, 7, 8], [7, 8, 9, 6, 4, 3, 5, 2, 1], [4, 7, 5, 3, 1, 2, 9, 8, 6], [9, 1, 3, 5, 8, 6, 7, 4, 2], [6, 2, 8, 7, 9, 4, 1, 3, 5], [3, 5, 6, 4, 7, 8, 2, 1, 9], [2, 4, 1, 9, 3, 5, 8, 6, 7], [8, 9, 7, 2, 6, 1, 3, 5, 4]]).
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.5.105",
6
+ "version": "1.5.107",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/iso.js CHANGED
@@ -674,22 +674,25 @@ function* termVariablesBuiltin({ goal, env }) {
674
674
  }
675
675
 
676
676
  function validPredicateIndicator(term) {
677
- // Each position is checked independently, not "could some single
678
- // instantiation satisfy both at once" -- so current_predicate(X/X)
679
- // (issue #115) passes this check exactly like current_predicate(X/Y)
680
- // does, and is then left to the ordinary group search below, which
681
- // unifies goal.args[0] (still X/X, the two positions aliased) against
682
- // each candidate Name/Arity in turn. Name and Arity are never the same
683
- // type, so that unification always fails and the goal simply finds no
684
- // solutions, the same way ISO's own current_op/3 (8.14.4.1) is specified:
685
- // its candidate set is built by unifying a triple's priority (an integer)
686
- // and specifier (an atom) against Priority and Op-specifier, so aliasing
687
- // those two arguments the same way makes every candidate fail to unify
688
- // and the set come up empty -- current_op/3's own Errors subclause
689
- // (8.14.4.3) has no special case for it, only domain errors for an
690
- // argument that is instantiated and outright wrong on its own. A
691
- // predicate_indicator type error remains for a term that cannot look
692
- // like a Name/Arity pair at all (a bare atom, a negative arity, ...).
677
+ // ISO 13211-1 8.8.2.3's sole error condition is "PI is neither a variable
678
+ // nor a predicate indicator -> type_error(predicate_indicator, PI)", and
679
+ // 8.8.2.4's own worked example -- current_predicate(Name/1) succeeds,
680
+ // backtracking through every matching Name -- shows that a compound whose
681
+ // Name is merely a variable already counts as "a predicate indicator" for
682
+ // this check, not just one whose Name and Arity are already bound to an
683
+ // atom and a non-negative integer as 7.1.6.6 defines the type. So each
684
+ // position is checked independently here, not "could some single
685
+ // instantiation satisfy both at once": current_predicate(X/X) (issue #115)
686
+ // passes this check exactly like current_predicate(X/Y) does, and is left
687
+ // to the ordinary group search below (8.8.2.1 a-c), which unifies
688
+ // goal.args[0] (still X/X, the two positions aliased) against each
689
+ // candidate Name/Arity in turn. Name and Arity are never the same type, so
690
+ // that unification always fails for every candidate and the goal simply
691
+ // finds no solutions -- 8.8.2.1's own procedure gives no other outcome for
692
+ // an empty candidate set than "the goal fails" (step c), with no separate
693
+ // provision for an argument that turned out unable to ever unify. A
694
+ // predicate_indicator type error remains for a term that cannot look like
695
+ // a Name/Arity pair at all (a bare atom, a negative arity, ...).
693
696
  return term.type === COMPOUND && term.name === '/' && term.arity === 2 &&
694
697
  (term.args[0].type === VAR || term.args[0].type === ATOM) &&
695
698
  (term.args[1].type === VAR ||
@@ -1592,19 +1592,30 @@ export function runIsoStrict(reporter = new TestReporter()) {
1592
1592
  const current = run('p(a).\nq.\n', { isoStrict: true, goal: 'current_predicate(p/1)' });
1593
1593
  equal(current.stats.completed_goal_lists, 1, 'current_predicate/1 finds a user procedure');
1594
1594
 
1595
- // The same variable filling both Name and Arity (issue #115, stc#79)
1596
- // looks valid position-by-position -- each alone accepts an unbound
1597
- // variable, and current_predicate/1 must support both fully unbound
1598
- // (current_predicate(X/Y), a generator) -- but no instantiation can
1599
- // satisfy both positions of one shared variable at once, since binding
1600
- // it to an atom makes Arity an atom too, not an integer. ISO does not
1601
- // special-case this for the structurally identical current_op/3
1602
- // (8.14.4.1): its candidate set is built by unifying a triple's integer
1603
- // priority and atom specifier against Priority and Op-specifier, so
1604
- // aliasing those two arguments the same way simply makes every
1605
- // candidate fail to unify and the set come up empty, and current_op/3's
1606
- // own Errors subclause (8.14.4.3) has no case for it. current_predicate
1607
- // /1 follows that precedent: it fails rather than raising an error.
1595
+ // ISO 13211-1 8.8.2.4's own worked example: current_predicate(Name/1)
1596
+ // succeeds with an unbound Name, backtracking through every arity-1
1597
+ // predicate. A compound with an unbound component already counts as "a
1598
+ // predicate indicator" for 8.8.2.3's error check, not only one whose
1599
+ // Name and Arity are already bound to an atom and a non-negative
1600
+ // integer as 7.1.6.6 defines the type.
1601
+ equal(
1602
+ run('elk(a).\ninsect(b).\n', { isoStrict: true, goal: 'current_predicate(Name/1)' }).stats.completed_goal_lists,
1603
+ 2,
1604
+ 'current_predicate/1 enumerates every matching Name with Arity fixed and Name unbound',
1605
+ );
1606
+
1607
+ // The same variable filling both Name and Arity (issue #115) looks
1608
+ // valid position-by-position by that same per-component check -- each
1609
+ // position alone accepts an unbound variable, and current_predicate/1
1610
+ // must support both fully unbound (current_predicate(X/Y), a generator,
1611
+ // just demonstrated above) -- but no instantiation can satisfy both
1612
+ // positions of one shared variable at once, since binding it to an atom
1613
+ // makes Arity an atom too, not an integer. 8.8.2.3 has only the one
1614
+ // error condition above, and 8.8.2.1's procedure gives no outcome for an
1615
+ // empty candidate set (step a-c) besides "the goal fails" (step c) --
1616
+ // there is no separate provision for an argument that turned out unable
1617
+ // to ever unify with anything. current_predicate/1 follows that: it
1618
+ // fails rather than raising an error.
1608
1619
  for (const [goal, label] of [
1609
1620
  ['current_predicate(X/X)', 'current_predicate/1 aliased name/arity variable fails'],
1610
1621
  ['X = Y, current_predicate(X/Y)', 'current_predicate/1 previously unified name/arity variables fails'],
@@ -4237,7 +4237,9 @@ be redundant. If partial choices already violate a constraint, pruning saves
4237
4237
  an entire subtree.
4238
4238
 
4239
4239
  This is why combinatorial examples are not toys. `clpz-n-queens.pl` exposes
4240
- the classic eight-queens search through finite-domain constraints. Together with `send-more-money.pl`,
4240
+ the classic N-queens search through finite-domain constraints -- genuinely
4241
+ searched at four queens; its eight-queens goal instead checks a known witness
4242
+ against the same model, without searching for it. Together with `send-more-money.pl`,
4241
4243
  `integer-partitions.pl`, `stirling-bell-numbers.pl`,
4242
4244
  and `weighted-interval-scheduling.pl`, they show different geometries of
4243
4245
  choice: permutations, digit assignments, recursive decompositions, set
@@ -9617,7 +9619,7 @@ eyeprolog --proof --goal 'type(X, Y)' examples/socrates.pl
9617
9619
  eyeprolog --proof examples/socrates.pl > socrates.why.pl
9618
9620
  eyeprolog --verify-proof socrates.why.pl examples/socrates.pl
9619
9621
  eyeprolog --warnings --goal 'answer(X)' test/conformance/warnings/negation/unstratified_mutual.pl
9620
- eyeprolog --portable --goal 'sudoku9_solution(S)' examples/clpz-sudoku-9x9.pl
9622
+ eyeprolog --portable --goal 'sudoku4_solution(S)' examples/clpz-sudoku-9x9.pl
9621
9623
  eyeprolog --stats --goal 'path(a, X)' examples/path-discovery.pl > answers.pl 2> run.stats
9622
9624
  ```
9623
9625
 
@@ -9984,7 +9986,7 @@ mode at a time.
9984
9986
  | [CLP(Z) global constraints](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-global-constraints.pl) | Compatibility tables, lexicographic and serialized schedules, global cardinality with costs, circuits, value counting, and integer comparison. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-global-constraints.pl) |
9985
9987
  | [CLP(Z) N-queens](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-n-queens.pl) | A checked eight-queens witness using finite domains, delayed diagonal constraints, `all_distinct/1`, and first-fail labeling, plus a four-queens multi-solution search. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-n-queens.pl) |
9986
9988
  | [CLP(Z) resource allocation](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-resource-allocation.pl) | Resource assignment using `element/3`, `sum/3`, `scalar_product/4`, reification, labeling options, and domain reflection. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-resource-allocation.pl) |
9987
- | [CLP(Z) Sudoku 9×9](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-sudoku-9x9.pl) | The AI Escargot 9×9 model with finite domains, 27 all-distinct constraints, and first-fail labeling; the default golden verifies its known solution while `sudoku9/1` remains the search relation. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-sudoku-9x9.pl) |
9989
+ | [CLP(Z) Sudoku 9×9](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-sudoku-9x9.pl) | The AI Escargot 9×9 model with finite domains, 27 all-distinct constraints, and first-fail labeling; a same-shaped 4×4 Shidoku is genuinely searched from its blanks as the routine default, while `sudoku9_witness/1` only checks the known 9×9 answer against the model (`sudoku9/1` itself remains the real, currently very slow 9×9 search relation). | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-sudoku-9x9.pl) |
9988
9990
  | [Combinatorics Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/combinatorics-findall-sort.pl) | Eyelet-inspired combinations example using `findall/3` and ISO `sort/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/combinatorics-findall-sort.pl) |
9989
9991
  | [Floating Point](https://github.com/eyereasoner/eyeprolog/blob/main/examples/floating-point.pl) | Floating-point arithmetic and comparisons. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/floating-point.pl) · [proof](https://github.com/eyereasoner/eyeprolog/blob/main/examples/proof/floating-point.pl) |
9990
9992
  | [Atomic conversion](https://github.com/eyereasoner/eyeprolog/blob/main/examples/iso-atomic-conversion.pl) | Atom splitting, character atoms, Unicode codes, and numeric parsing. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/iso-atomic-conversion.pl) |