eyeprolog 1.3.9 → 1.3.10

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.9",
6
+ "version": "1.3.10",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/src/program.js CHANGED
@@ -314,15 +314,10 @@ export class Program {
314
314
  const wfsNegativeEdges = [];
315
315
  for (const group of groups) {
316
316
  const groupIndex = indexByGroup.get(group);
317
+ let compactDependencies = null;
317
318
  for (const clause of group.clauses) {
318
319
  if (isCompactBinaryClause(clause)) {
319
- if (clause.bodyName != null) {
320
- const dep = this.findGroup(clause.bodyName, 2, group.module);
321
- if (dep) {
322
- deps[groupIndex].add(indexByGroup.get(dep));
323
- cutDeps[groupIndex].add(indexByGroup.get(dep));
324
- }
325
- }
320
+ if (clause.bodyName != null) (compactDependencies ??= new Set()).add(clause.bodyName);
326
321
  continue;
327
322
  }
328
323
  for (const goal of clause.body) {
@@ -349,6 +344,13 @@ export class Program {
349
344
  }
350
345
  }
351
346
  }
347
+ for (const name of compactDependencies ?? []) {
348
+ const dep = this.findGroup(name, 2, group.module);
349
+ if (!dep) continue;
350
+ const dependencyIndex = indexByGroup.get(dep);
351
+ deps[groupIndex].add(dependencyIndex);
352
+ cutDeps[groupIndex].add(dependencyIndex);
353
+ }
352
354
  }
353
355
  const finiteDatalogCache = new Map();
354
356
  const rangeRestrictedDatalogCache = new Map();
@@ -1301,12 +1303,10 @@ function datalogDependencyClauseCount(program, group, seen = new Set()) {
1301
1303
  if (seen.has(group)) return 0;
1302
1304
  seen.add(group);
1303
1305
  let count = group.clauses.length;
1306
+ let compactDependencies = null;
1304
1307
  for (const clause of group.clauses) {
1305
1308
  if (isCompactBinaryClause(clause)) {
1306
- if (clause.bodyName != null) {
1307
- const target = program.findGroup(clause.bodyName, 2, group.module);
1308
- if (target) count += datalogDependencyClauseCount(program, target, seen);
1309
- }
1309
+ if (clause.bodyName != null) (compactDependencies ??= new Set()).add(clause.bodyName);
1310
1310
  continue;
1311
1311
  }
1312
1312
  for (const goal of clause.body) {
@@ -1315,6 +1315,10 @@ function datalogDependencyClauseCount(program, group, seen = new Set()) {
1315
1315
  if (target) count += datalogDependencyClauseCount(program, target, seen);
1316
1316
  }
1317
1317
  }
1318
+ for (const name of compactDependencies ?? []) {
1319
+ const target = program.findGroup(name, 2, group.module);
1320
+ if (target) count += datalogDependencyClauseCount(program, target, seen);
1321
+ }
1318
1322
  return count;
1319
1323
  }
1320
1324
 
@@ -1324,12 +1328,10 @@ function isFiniteDatalogGroup(program, group, cache = new Map(), visiting = new
1324
1328
  if (visiting.has(group)) return true;
1325
1329
  visiting.add(group);
1326
1330
  let finite = true;
1331
+ let compactDependencies = null;
1327
1332
  for (const clause of group.clauses) {
1328
1333
  if (isCompactBinaryClause(clause)) {
1329
- if (clause.bodyName != null) {
1330
- const target = program.findGroup(clause.bodyName, 2, group.module);
1331
- if (!target || !isFiniteDatalogGroup(program, target, cache, visiting)) { finite = false; break; }
1332
- }
1334
+ if (clause.bodyName != null) (compactDependencies ??= new Set()).add(clause.bodyName);
1333
1335
  continue;
1334
1336
  }
1335
1337
  if (clause.head.type === COMPOUND && !clause.head.args.every(isFiniteDatalogArgument)) { finite = false; break; }
@@ -1345,6 +1347,12 @@ function isFiniteDatalogGroup(program, group, cache = new Map(), visiting = new
1345
1347
  }
1346
1348
  if (!finite) break;
1347
1349
  }
1350
+ if (finite) {
1351
+ for (const name of compactDependencies ?? []) {
1352
+ const target = program.findGroup(name, 2, group.module);
1353
+ if (!target || !isFiniteDatalogGroup(program, target, cache, visiting)) { finite = false; break; }
1354
+ }
1355
+ }
1348
1356
  visiting.delete(group);
1349
1357
  cache.set(group, finite);
1350
1358
  return finite;
@@ -1358,7 +1366,35 @@ function isRangeRestrictedFiniteDatalogGroup(program, group, cache = new Map(),
1358
1366
  visiting.add(group);
1359
1367
  let finite = true;
1360
1368
 
1369
+ let compactDependencyResults = null;
1361
1370
  for (const clause of group.clauses) {
1371
+ if (isCompactBinaryClause(clause)) {
1372
+ const head0Variable = clause.head0Type === VAR;
1373
+ const head1Variable = clause.head1Type === VAR;
1374
+ if (clause.bodyName == null) {
1375
+ if (head0Variable || head1Variable) { finite = false; break; }
1376
+ continue;
1377
+ }
1378
+ const head0RangeRestricted = !head0Variable ||
1379
+ (clause.body0Type === VAR && clause.body0Name === clause.head0Name) ||
1380
+ (clause.body1Type === VAR && clause.body1Name === clause.head0Name);
1381
+ const head1RangeRestricted = !head1Variable ||
1382
+ (clause.body0Type === VAR && clause.body0Name === clause.head1Name) ||
1383
+ (clause.body1Type === VAR && clause.body1Name === clause.head1Name);
1384
+ if (!head0RangeRestricted || !head1RangeRestricted) {
1385
+ finite = false;
1386
+ break;
1387
+ }
1388
+ let targetFinite = compactDependencyResults?.get(clause.bodyName);
1389
+ if (targetFinite == null) {
1390
+ const target = program.findGroup(clause.bodyName, 2, group.module);
1391
+ targetFinite = target != null && isRangeRestrictedFiniteDatalogGroup(program, target, cache, visiting);
1392
+ (compactDependencyResults ??= new Map()).set(clause.bodyName, targetFinite);
1393
+ }
1394
+ if (!targetFinite) { finite = false; break; }
1395
+ continue;
1396
+ }
1397
+
1362
1398
  const head = clause.head;
1363
1399
  if ((head.type !== COMPOUND && head.type !== ATOM) ||
1364
1400
  (head.type === COMPOUND && !head.args.every(isFiniteDatalogArgument))) {
@@ -4484,6 +4484,23 @@ function whiteBoxCases() {
4484
4484
  assertEqual(ground.stats.datalog_evaluations, 0, 'ground query keeps the ordinary indexed chain path');
4485
4485
  },
4486
4486
  },
4487
+ {
4488
+ name: 'compact finite Datalog planning preserves lazy clause terms',
4489
+ run: () => {
4490
+ const facts = Array.from({ length: 130 }, (_, i) => `compact_edge(n${i}, n${i + 1}).`).join('\n');
4491
+ const source = `${facts}\ncompact_edge(X,Y) :- compact_edge(X,Y).\n`;
4492
+ const program = Program.parseSources([{ text: source, filename: 'compact-datalog.pl' }], {
4493
+ sourceMetadata: false,
4494
+ });
4495
+ const group = program.findGroup('compact_edge', 2);
4496
+ assertEqual(group.datalogLeastModel, true, 'compact recursive Datalog remains eligible');
4497
+ assertEqual(
4498
+ group.clauses.filter((clause) => clause._head != null || clause._body != null).length,
4499
+ 0,
4500
+ 'planning keeps compact clause terms lazy',
4501
+ );
4502
+ },
4503
+ },
4487
4504
  {
4488
4505
  name: 'findall length counting preserves multiplicity and observable bags',
4489
4506
  run: () => {