eyeling 1.7.14 → 1.7.15

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/eyeling.js +51 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -219,7 +219,7 @@ Commonly used N3/Turtle features:
219
219
  - **log**: `log:collectAllIn` `log:content` `log:dtlit` `log:equalTo` `log:forAllIn` `log:impliedBy` `log:implies` `log:includes` `log:langlit` `log:notEqualTo` `log:notIncludes` `log:outputString` `log:parsedAsN3` `log:rawType` `log:semantics` `log:semanticsOrError` `log:skolem` `log:uri`
220
220
  - **math**: `math:absoluteValue` `math:acos` `math:asin` `math:atan` `math:cos` `math:cosh` `math:degrees` `math:difference` `math:equalTo` `math:exponentiation` `math:greaterThan` `math:integerQuotient` `math:lessThan` `math:negation` `math:notEqualTo` `math:notGreaterThan` `math:notLessThan` `math:product` `math:quotient` `math:remainder` `math:rounded` `math:sin` `math:sinh` `math:sum` `math:tan` `math:tanh`
221
221
  - **string**: `string:concatenation` `string:contains` `string:containsIgnoringCase` `string:endsWith` `string:equalIgnoringCase` `string:format` `string:greaterThan` `string:jsonPointer` `string:lessThan` `string:matches` `string:notEqualIgnoringCase` `string:notGreaterThan` `string:notLessThan` `string:notMatches` `string:replace` `string:scrape` `string:startsWith`
222
- - **time**: `time:day` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
222
+ - **time**: `time:day` `time:hour` `time:localTime` `time:minute` `time:month` `time:second` `time:timeZone` `time:year`
223
223
 
224
224
  ## License
225
225
 
package/eyeling.js CHANGED
@@ -4131,7 +4131,7 @@ function evalCryptoHashBuiltin(g, subst, algo) {
4131
4131
  // ===========================================================================
4132
4132
  // Backward proof & builtins mutual recursion — declarations first
4133
4133
 
4134
- function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
4134
+ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4135
4135
  const g = applySubstTriple(goal, subst);
4136
4136
  const pv = iriValue(g.p);
4137
4137
  if (pv === null) return null;
@@ -5648,7 +5648,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
5648
5648
 
5649
5649
  const visited2 = [];
5650
5650
  // Start from the incoming substitution so bindings flow outward.
5651
- return proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen);
5651
+ return proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen, maxResults);
5652
5652
  }
5653
5653
 
5654
5654
  // log:notIncludes
@@ -5669,7 +5669,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen) {
5669
5669
  });
5670
5670
 
5671
5671
  const visited2 = [];
5672
- const sols = proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen);
5672
+ const sols = proveGoals(Array.from(g.o.triples), { ...subst }, scopeFacts, [], depth + 1, visited2, varGen, 1);
5673
5673
  return sols.length ? [] : [{ ...subst }];
5674
5674
  }
5675
5675
 
@@ -6244,10 +6244,11 @@ function maybeCompactSubst(subst, goals, answerVars, depth) {
6244
6244
  return gcCompactForGoals(subst, goals, answerVars);
6245
6245
  }
6246
6246
 
6247
- function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6247
+ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen, maxResults) {
6248
6248
  // Iterative DFS over proof states using an explicit stack.
6249
6249
  // Each state carries its own substitution and remaining goals.
6250
6250
  const results = [];
6251
+ const max = typeof maxResults === 'number' && maxResults > 0 ? maxResults : Infinity;
6251
6252
 
6252
6253
  const initialGoals = Array.isArray(goals) ? goals.slice() : [];
6253
6254
  const initialSubst = subst ? { ...subst } : {};
@@ -6258,6 +6259,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6258
6259
  gcCollectVarsInGoals(initialGoals, answerVars);
6259
6260
  if (!initialGoals.length) {
6260
6261
  results.push(gcCompactForGoals(initialSubst, [], answerVars));
6262
+
6263
+ if (results.length >= max) return results;
6261
6264
  return results;
6262
6265
  }
6263
6266
 
@@ -6275,6 +6278,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6275
6278
 
6276
6279
  if (!state.goals.length) {
6277
6280
  results.push(gcCompactForGoals(state.subst, [], answerVars));
6281
+
6282
+ if (results.length >= max) return results;
6278
6283
  continue;
6279
6284
  }
6280
6285
 
@@ -6284,13 +6289,18 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6284
6289
 
6285
6290
  // 1) Builtins
6286
6291
  if (isBuiltinPred(goal0.p)) {
6287
- const deltas = evalBuiltin(goal0, {}, facts, backRules, state.depth, varGen);
6292
+ const remaining = max - results.length;
6293
+ if (remaining <= 0) return results;
6294
+ const builtinMax = Number.isFinite(remaining) && !restGoals.length ? remaining : undefined;
6295
+ const deltas = evalBuiltin(goal0, {}, facts, backRules, state.depth, varGen, builtinMax);
6288
6296
  const nextStates = [];
6289
6297
  for (const delta of deltas) {
6290
6298
  const composed = composeSubst(state.subst, delta);
6291
6299
  if (composed === null) continue;
6292
6300
  if (!restGoals.length) {
6293
6301
  results.push(gcCompactForGoals(composed, [], answerVars));
6302
+
6303
+ if (results.length >= max) return results;
6294
6304
  } else {
6295
6305
  const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
6296
6306
  nextStates.push({
@@ -6321,6 +6331,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6321
6331
  if (composed === null) continue;
6322
6332
  if (!restGoals.length) {
6323
6333
  results.push(gcCompactForGoals(composed, [], answerVars));
6334
+
6335
+ if (results.length >= max) return results;
6324
6336
  } else {
6325
6337
  const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
6326
6338
  nextStates.push({
@@ -6342,6 +6354,8 @@ function proveGoals(goals, subst, facts, backRules, depth, visited, varGen) {
6342
6354
  if (composed === null) continue;
6343
6355
  if (!restGoals.length) {
6344
6356
  results.push(gcCompactForGoals(composed, [], answerVars));
6357
+
6358
+ if (results.length >= max) return results;
6345
6359
  } else {
6346
6360
  const nextSubst = maybeCompactSubst(composed, restGoals, answerVars, state.depth + 1);
6347
6361
  nextStates.push({
@@ -6454,7 +6468,38 @@ function forwardChain(facts, forwardRules, backRules, onDerived /* optional */)
6454
6468
  const r = forwardRules[i];
6455
6469
  const empty = {};
6456
6470
  const visited = [];
6457
- const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen);
6471
+ // Optimization: if the rule head is **structurally ground** (no vars anywhere, even inside
6472
+ // quoted formulas) and has no head blanks, then the head does not depend on which body
6473
+ // solution we pick. In that case, we only need *one* proof of the body, and once all head
6474
+ // triples are already known we can skip proving the body entirely.
6475
+ function isStrictGroundTerm(t) {
6476
+ if (t instanceof Var) return false;
6477
+ if (t instanceof Blank) return false;
6478
+ if (t instanceof OpenListTerm) return false;
6479
+ if (t instanceof ListTerm) return t.elems.every(isStrictGroundTerm);
6480
+ if (t instanceof GraphTerm) return t.triples.every(isStrictGroundTriple);
6481
+ return true; // Iri/Literal and any other atomic terms
6482
+ }
6483
+ function isStrictGroundTriple(tr) {
6484
+ return isStrictGroundTerm(tr.s) && isStrictGroundTerm(tr.p) && isStrictGroundTerm(tr.o);
6485
+ }
6486
+
6487
+ const headIsStrictGround =
6488
+ !r.isFuse && (!r.headBlankLabels || r.headBlankLabels.size === 0) && r.conclusion.every(isStrictGroundTriple);
6489
+
6490
+ if (headIsStrictGround) {
6491
+ let allKnown = true;
6492
+ for (const tr of r.conclusion) {
6493
+ if (!hasFactIndexed(facts, tr)) {
6494
+ allKnown = false;
6495
+ break;
6496
+ }
6497
+ }
6498
+ if (allKnown) continue;
6499
+ }
6500
+
6501
+ const maxSols = (r.isFuse || headIsStrictGround) ? 1 : undefined;
6502
+ const sols = proveGoals(r.premise.slice(), empty, facts, backRules, 0, visited, varGen, maxSols);
6458
6503
 
6459
6504
  // Inference fuse
6460
6505
  if (r.isFuse && sols.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.7.14",
3
+ "version": "1.7.15",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [