eyeprolog 1.5.24 → 1.5.25
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/examples/odrl-policy-reasoning.pl +3 -3
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/solver.js +12 -3
- package/test/run-regression.mjs +19 -3
- package/the-art-of-eyeprolog.md +5 -4
|
@@ -345,11 +345,11 @@ profile_prohibition(Request) :-
|
|
|
345
345
|
cycle_request(Request),
|
|
346
346
|
tnot(profile_permission(Request)).
|
|
347
347
|
|
|
348
|
-
% For a ground WFS claim,
|
|
349
|
-
% the truth value is undefined.
|
|
348
|
+
% For a ground WFS claim, true and false are successful positive and negative
|
|
349
|
+
% probes. If neither probe succeeds, the WFS truth value is undefined.
|
|
350
350
|
wfs_truth(Claim, true) :- call(Claim), \+ tnot(Claim).
|
|
351
351
|
wfs_truth(Claim, false) :- tnot(Claim), \+ call(Claim).
|
|
352
|
-
wfs_truth(Claim, undefined) :- call(Claim), tnot(Claim).
|
|
352
|
+
wfs_truth(Claim, undefined) :- \+ call(Claim), \+ tnot(Claim).
|
|
353
353
|
|
|
354
354
|
% --- Curated questions -----------------------------------------------------
|
|
355
355
|
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface EyePrologStats {
|
|
2
2
|
/** Alternating-fixed-point rounds used to build WFS models. */
|
|
3
3
|
wfs_fixpoint_rounds: number;
|
|
4
|
-
/** Undefined WFS
|
|
4
|
+
/** Undefined WFS atoms encountered while resolving query goals. */
|
|
5
5
|
wfs_undefined_answers: number;
|
|
6
6
|
[key: string]: number;
|
|
7
7
|
}
|
package/package.json
CHANGED
package/src/solver.js
CHANGED
|
@@ -909,7 +909,10 @@ export class Solver {
|
|
|
909
909
|
if (!unify(goal.args[i], row[i], next)) { ok = false; break; }
|
|
910
910
|
}
|
|
911
911
|
if (!ok) continue;
|
|
912
|
-
if (!lower?.has(row))
|
|
912
|
+
if (!lower?.has(row)) {
|
|
913
|
+
this.stats.wfs_undefined_answers++;
|
|
914
|
+
continue;
|
|
915
|
+
}
|
|
913
916
|
yield* this.solve(rest, next, depth + 1);
|
|
914
917
|
if (this.solutionsSeen >= this.solutionLimit) return;
|
|
915
918
|
}
|
|
@@ -927,7 +930,10 @@ export class Solver {
|
|
|
927
930
|
const model = this.wfsModelFor(group);
|
|
928
931
|
const truth = truthOfGroundGoal(model, invoked);
|
|
929
932
|
if (truth === 'true') return;
|
|
930
|
-
if (truth === 'undefined')
|
|
933
|
+
if (truth === 'undefined') {
|
|
934
|
+
this.stats.wfs_undefined_answers++;
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
931
937
|
yield env;
|
|
932
938
|
return;
|
|
933
939
|
}
|
|
@@ -1523,7 +1529,10 @@ function pushWfsAnswerFrames(stack, model, group, goal, rest, env, depth, active
|
|
|
1523
1529
|
if (!unify(goal.args[i], row[i], next)) { ok = false; break; }
|
|
1524
1530
|
}
|
|
1525
1531
|
if (!ok) continue;
|
|
1526
|
-
if (!lower?.has(row))
|
|
1532
|
+
if (!lower?.has(row)) {
|
|
1533
|
+
solver.stats.wfs_undefined_answers++;
|
|
1534
|
+
continue;
|
|
1535
|
+
}
|
|
1527
1536
|
stack.push({ kind: 'goals', goals: rest, env: next, depth: depth + 1, active });
|
|
1528
1537
|
}
|
|
1529
1538
|
}
|
package/test/run-regression.mjs
CHANGED
|
@@ -7782,9 +7782,9 @@ win(X) :- move(X, Y), tnot(win(Y)).
|
|
|
7782
7782
|
assertEqual(program.findGroup('win', 1).wfsDatalog, true, 'win/1 uses finite WFS evaluation');
|
|
7783
7783
|
assertEqual(program.findGroup('win', 1).tabled, false, 'win/1 is not positive least-model tabled');
|
|
7784
7784
|
const result = run(program, { goal: 'win(X)' });
|
|
7785
|
-
assertEqual(result.stdout
|
|
7785
|
+
assertEqual(result.stdout, '', 'negative cycle does not expose undefined atoms as successful answers');
|
|
7786
7786
|
assertEqual(result.stats.wfs_fixpoint_rounds, 2, 'alternating fixed point converges in two rounds');
|
|
7787
|
-
assertEqual(result.stats.wfs_undefined_answers, 4, 'cycle
|
|
7787
|
+
assertEqual(result.stats.wfs_undefined_answers, 4, 'cycle atoms are observed as undefined rather than unconditional truths');
|
|
7788
7788
|
|
|
7789
7789
|
const nafProgram = Program.parse('p(X) :- \\+ q(X).\nq(X) :- p(X).\n');
|
|
7790
7790
|
assertEqual(nafProgram.findGroup('p', 1).wfsDatalog, false, '\\+/1 remains ordinary negation as failure');
|
|
@@ -7804,7 +7804,23 @@ undefined_case :- tnot(win(c)).
|
|
|
7804
7804
|
`;
|
|
7805
7805
|
assertEqual(run(source, { goal: 'true_case' }).stdout, 'true_case.\n', 'win(a) is true because b is false');
|
|
7806
7806
|
assertEqual(run(source, { goal: 'false_case' }).stdout, '', 'tnot(win(a)) fails for a true atom');
|
|
7807
|
-
assertEqual(run(source, { goal: 'undefined_case' }).stdout, '
|
|
7807
|
+
assertEqual(run(source, { goal: 'undefined_case' }).stdout, '', 'tnot of an undefined WFS atom is not a successful answer');
|
|
7808
|
+
},
|
|
7809
|
+
},
|
|
7810
|
+
{
|
|
7811
|
+
name: 'undefined WFS goals do not run continuations or print CLI answers',
|
|
7812
|
+
run: () => {
|
|
7813
|
+
const source = `
|
|
7814
|
+
:- use_module(library(tabling)).
|
|
7815
|
+
:- table(a/0).
|
|
7816
|
+
:- table(b/0).
|
|
7817
|
+
a :- tnot(b).
|
|
7818
|
+
b :- tnot(a).
|
|
7819
|
+
`;
|
|
7820
|
+
const result = run(source, { goal: 'a, write(true), nl; halt' });
|
|
7821
|
+
assertEqual(result.stdout, '', 'undefined first branch neither writes nor produces a ground answer');
|
|
7822
|
+
assertEqual(result.haltCode, 0, 'fallback halt branch runs');
|
|
7823
|
+
assertEqual(result.stats.wfs_undefined_answers, 1, 'undefined atom remains observable in statistics');
|
|
7808
7824
|
},
|
|
7809
7825
|
},
|
|
7810
7826
|
{
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -1144,8 +1144,9 @@ win(X) :- move(X, Y), tnot(win(Y)).
|
|
|
1144
1144
|
```
|
|
1145
1145
|
|
|
1146
1146
|
Here neither `win(a)` nor `win(b)` is unconditionally established; both belong
|
|
1147
|
-
to the undefined part of the well-founded model. EyeProlog
|
|
1148
|
-
|
|
1147
|
+
to the undefined part of the well-founded model. EyeProlog retains that state
|
|
1148
|
+
internally but does not expose undefined atoms as successful query answers or
|
|
1149
|
+
run the remaining goals of a conjunction through them.
|
|
1149
1150
|
Direct calls to `tnot/1` must be ground. In WFS rules, variables occurring in
|
|
1150
1151
|
the head or a negated literal must be range-restricted by positive body
|
|
1151
1152
|
literals. Ordinary `\+/1` is unchanged, and strict ISO mode does not provide
|
|
@@ -1664,8 +1665,8 @@ The reported counters include completed goal lists, calls to the goal solver
|
|
|
1664
1665
|
and single-goal solver, unification attempts, maximum depth and goal-list size,
|
|
1665
1666
|
deterministic built-in successes and failures, and table fixed-point rounds.
|
|
1666
1667
|
WFS execution additionally reports `wfs_fixpoint_rounds` and
|
|
1667
|
-
`wfs_undefined_answers`. The latter counts undefined
|
|
1668
|
-
|
|
1668
|
+
`wfs_undefined_answers`. The latter counts undefined atoms encountered while
|
|
1669
|
+
resolving query goals; it is an execution statistic, not a declaration
|
|
1669
1670
|
that those atoms are true. All statistics describe work performed, not logical
|
|
1670
1671
|
truth. Compare counters only across equivalent queries and the same
|
|
1671
1672
|
implementation version.
|