gdp-wired 0.17.0 → 0.19.0

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.
@@ -781,6 +781,7 @@ export declare class LoopRule<P extends Obj = {}, I extends Obj = {}, S extends
781
781
  law(name: string, f: (env: P & S) => Cond): Law;
782
782
  private env;
783
783
  get loop(): Loop;
784
+ get hasRank(): boolean;
784
785
  get rankTerms(): E[];
785
786
  randomParams(rand: Rand, bound: number): Record<string, number>;
786
787
  randomInput(rand: Rand, bound: number): Record<string, number>;
@@ -810,10 +811,13 @@ export type Found = {
810
811
  lemmas?: string[];
811
812
  between?: string[];
812
813
  }>;
814
+ facts?: string[];
815
+ rank?: string;
813
816
  };
814
817
  export type ProveOptions = {
815
818
  search?: boolean | {
816
819
  ms?: number;
820
+ cycles?: boolean;
817
821
  };
818
822
  };
819
823
  export declare class Unproven extends Error {
@@ -834,7 +838,9 @@ export declare function crossCheckDecision(d: Decision, opts?: {
834
838
  checked: number;
835
839
  failure?: string;
836
840
  };
837
- export declare const proveLoop: (l: LoopRule<Obj, Obj, Obj>, laws: Law[]) => Verdict;
841
+ export declare const proveLoop: (l: LoopRule<Obj, Obj, Obj>, laws: Law[], opts?: {
842
+ ms?: number;
843
+ }) => Verdict;
838
844
  export type TreeValue<I> = {
839
845
  leaf: I;
840
846
  } | {
package/dist/core/dsl.js CHANGED
@@ -18,10 +18,11 @@
18
18
  import { carriedView, compileFunction, evalC, evalE, interpret, loadJs, numbersOf, pairedArgs } from "./machine.js";
19
19
  import { allowed, keysOf, requestEnv, watch } from "./watch.js";
20
20
  import { check, foldObligations, loopObligations } from "../kernel/kernel.js";
21
- import { factsFor, grow, proveLaws, proveLiveness, proveStepLaws, withDeadline } from "../kernel/prove.js";
22
- import { crossCyclic, cyclicOn, foldNodes, proveCyclic, quietly, recurNodes } from "./cyclic.js";
21
+ import { factsFor, grow, proveLaws, proveLiveness, proveStepLaws, usualProof, withDeadline } from "../kernel/prove.js";
22
+ import { certCyclic, certOn, crossCyclic, cyclicOn, cycLog, foldNodes, proveCyclic, quietly, recurNodes, translatedProof } from "./cyclic.js";
23
23
  import { discover } from "./discover.js";
24
24
  import { inferLaws } from "./infer.js";
25
+ import { cycleLemmas, findRank } from "./cycles.js";
25
26
  // --- terms and conditions -------------------------------------------------------------------------------------------
26
27
  const naturalConst = (v) => {
27
28
  if (!Number.isSafeInteger(v) || v < 0)
@@ -1409,6 +1410,10 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
1409
1410
  // The lemmas: x counting up from 0 and fx its value, each lemma a law of that machine.
1410
1411
  let verdict = { proven: true, checked: true };
1411
1412
  const proofs = {};
1413
+ // The usual proofs, to translate (core/cyclic.ts's recurNodes): the lemmas' machine's, the laws between's.
1414
+ let lemmaProof;
1415
+ const translatedRecur = (u) => (u.lemmas || !stated.length)
1416
+ ? st => translatedProof(() => JSON.stringify(['recurrence', recurCase(u.between ? pairs : [])]), () => recurNodes(recurCase(u.between ? pairs : []), u, st), st) : undefined;
1412
1417
  if (stated.length) {
1413
1418
  // A start that reads a function is a param given as that start by a fact (a start reads the params alone).
1414
1419
  const zeroReads = JSON.stringify(zero).includes('"op":"get"');
@@ -1420,9 +1425,13 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
1420
1425
  const laws = Object.entries(lemmas).map(([n, f]) => r.law(n, (env) => f(env.x, env.fx, envAt(args.map(a => env[a])))));
1421
1426
  // (Its machine's proof timed alone: the recurrence is compared below as its own instance.)
1422
1427
  const t0 = performance.now();
1428
+ usualProof.last = undefined;
1423
1429
  verdict = quietly(() => proveRule(r, laws, { search: false }));
1430
+ lemmaProof = usualProof.last;
1424
1431
  if (cyclicOn())
1425
- crossCyclic('recurrence', name, verdict.proven && verdict.checked, () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0);
1432
+ crossCyclic('recurrence', name, verdict.proven && verdict.checked, () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0, translatedRecur({ lemmas: lemmaProof }));
1433
+ if (certOn() && verdict.proven && verdict.checked)
1434
+ verdict = { ...verdict, checked: certCyclic('recurrence', name, translatedRecur({ lemmas: lemmaProof }), () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0).checked };
1426
1435
  failed('lemmas', verdict);
1427
1436
  proofs.lemmas = { machine: r.machine, laws };
1428
1437
  }
@@ -1459,9 +1468,12 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
1459
1468
  .step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(at(env))) }));
1460
1469
  const laws = [...Object.entries(lemmas).map(([n, l]) => r.law(`${n} at y`, (env) => l(env.y, env.fy, envAt(at(env))))),
1461
1470
  ...Object.entries(between).map(([n, l]) => r.law(n, (env) => l(env.x, env.fx, env.y, env.fy, envAt(at(env)))))];
1462
- const t0 = performance.now(), v = quietly(() => proveRule(r, laws, { search: false })), usualMs = performance.now() - t0;
1471
+ usualProof.last = undefined;
1472
+ const t0 = performance.now(), v = quietly(() => proveRule(r, laws, { search: false })), usualMs = performance.now() - t0, betweenProof = usualProof.last;
1463
1473
  if (cyclicOn())
1464
- crossCyclic('recurrence, between', name, v.proven && v.checked, () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs);
1474
+ crossCyclic('recurrence, between', name, v.proven && v.checked, () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs, betweenProof && translatedRecur({ lemmas: lemmaProof, between: betweenProof }));
1475
+ if (certOn() && v.proven && v.checked)
1476
+ v.checked = certCyclic('recurrence, between', name, betweenProof && translatedRecur({ lemmas: lemmaProof, between: betweenProof }), () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs).checked;
1465
1477
  failed('laws between two points', v);
1466
1478
  proofs.between = { machine: r.machine, laws };
1467
1479
  verdict = { proven: true, checked: verdict.checked && v.checked };
@@ -2483,6 +2495,7 @@ export class LoopRule {
2483
2495
  ...(assume.length ? { assume: assume.length === 1 ? assume[0] : { op: 'and', xs: assume } } : {}),
2484
2496
  };
2485
2497
  }
2498
+ get hasRank() { return !!this.spec.rank; }
2486
2499
  get rankTerms() {
2487
2500
  if (!this.spec.rank)
2488
2501
  throw new Error(`${this.spec.name}: no rank`);
@@ -2502,7 +2515,7 @@ export class Unproven extends Error {
2502
2515
  this.verdict = verdict;
2503
2516
  }
2504
2517
  }
2505
- const goalsVerdict = (goals) => {
2518
+ const goalsVerdict = (goals, checkIt = true) => {
2506
2519
  const trees = {};
2507
2520
  for (const g of goals) {
2508
2521
  const out = grow(g);
@@ -2510,7 +2523,7 @@ const goalsVerdict = (goals) => {
2510
2523
  return { proven: false, checked: false, goal: g.name, stuck: out.stuck, ...(out.witness ? { witness: out.witness } : {}), ...(out.why ? { why: out.why } : {}) };
2511
2524
  trees[g.name] = out.tree;
2512
2525
  }
2513
- return { proven: true, checked: goals.every(g => check(g, trees[g.name])), trees };
2526
+ return { proven: true, checked: !checkIt || goals.every(g => check(g, trees[g.name])), trees };
2514
2527
  };
2515
2528
  // The kinds' laws first, those the caller didn't pass already (by name).
2516
2529
  const withKinds = (r, laws) => [...r.kindLaws().filter(k => !laws.some(l => l.name === k.name)), ...laws];
@@ -2624,6 +2637,13 @@ function searched(r, laws, stepLaws, v, opts) {
2624
2637
  if (Object.keys(d.found).length)
2625
2638
  (rule = d.rule, lemmas = d.found);
2626
2639
  }
2640
+ // Functions known by their facts alone (not a recurrence): lemmas about them, fitted to their values and proven from
2641
+ // the facts as cyclic claims, the laws proven with them as facts.
2642
+ if ((typeof opts.search !== 'object' || opts.search.cycles !== false) && fns.some(([, d]) => !recurOf(d)) && rule.machine.facts?.length && until - performance.now() > 500) {
2643
+ const c = withDeadline(until, () => cycleLemmas(rule.machine, withKinds(rule, laws), stepLaws, { ms: (until - performance.now()) / 2 }));
2644
+ if (c.proven && c.checked)
2645
+ return { proven: true, checked: true, found: { ...(lemmas ? { lemmas } : {}), facts: c.found.map(f => f.text) } };
2646
+ }
2627
2647
  const ms = until - performance.now();
2628
2648
  if (ms < 500)
2629
2649
  return v;
@@ -2667,7 +2687,17 @@ export function crossCheckDecision(d, opts = {}) {
2667
2687
  }
2668
2688
  return { checked };
2669
2689
  }
2670
- export const proveLoop = (l, laws) => goalsVerdict(loopObligations(l.loop, laws, l.rankTerms));
2690
+ // With no rank stated, the rank is searched for from the loop's runs (core/cycles.ts's findRank), with the invariants it
2691
+ // needs (inferLaws), within `ms` (10 s); the verdict is the kernel's on the rank and the laws found, in `found`.
2692
+ export const proveLoop = (l, laws, opts = {}) => {
2693
+ if (cyclicOn() || certOn())
2694
+ cycLog({ kind: 'loop', name: l.loop.name, uncovered: true, why: 'a claim about runs that never end' });
2695
+ if (l.hasRank)
2696
+ return goalsVerdict(loopObligations(l.loop, laws, l.rankTerms));
2697
+ const out = findRank(l.loop, laws, opts);
2698
+ return { proven: out.proven, checked: out.checked, ...(out.trees ? { trees: out.trees } : {}), ...(out.goal ? { goal: out.goal, stuck: out.stuck } : {}), ...(out.witness ? { witness: out.witness } : {}), ...(out.why ? { why: out.why } : {}),
2699
+ ...(out.found ? { found: { rank: out.found.rank, ...(out.laws?.length ? { laws: out.laws.map((law, i) => ({ law, text: out.found.laws[i] ?? law.name })) } : {}) } } : {}) };
2700
+ };
2671
2701
  export class FoldRule {
2672
2702
  spec;
2673
2703
  constructor(spec) {
@@ -2783,9 +2813,12 @@ export class FoldRule {
2783
2813
  export const fold = (name) => new FoldRule({ name, params: [], inputs: [], pre: [], assume: [] });
2784
2814
  // A fold's laws proven over every tree (a rose fold's own laws, its children's lemmas, with them).
2785
2815
  export const proveFold = (f, laws) => {
2786
- const all = [...(f.lemmas ?? []), ...laws], t0 = performance.now(), v = goalsVerdict(foldObligations(f.fold, all)), usualMs = performance.now() - t0;
2816
+ // (GDP_CERT=cyclic: the certificate a cyclic proof, core/cyclic.ts's certCyclic, the usual trees its input)
2817
+ const all = [...(f.lemmas ?? []), ...laws], t0 = performance.now(), v = goalsVerdict(foldObligations(f.fold, all), !certOn()), usualMs = performance.now() - t0;
2787
2818
  if (cyclicOn())
2788
- crossCyclic('fold', f.fold.name, v.proven && v.checked, () => { const { facts, specs } = foldNodes(f.fold, all); return proveCyclic(facts, specs); }, usualMs);
2819
+ crossCyclic('fold', f.fold.name, v.proven && v.checked, () => { const { facts, specs } = foldNodes(f.fold, all); return proveCyclic(facts, specs); }, usualMs, v.trees && (st => translatedProof(() => JSON.stringify(['fold', f.fold, all]), () => foldNodes(f.fold, all, v.trees, st), st)));
2820
+ if (certOn() && v.proven)
2821
+ v.checked = certCyclic('fold', f.fold.name, v.trees && (st => translatedProof(() => JSON.stringify(['fold', f.fold, all]), () => foldNodes(f.fold, all, v.trees, st), st)), () => { const { facts, specs } = foldNodes(f.fold, all); return proveCyclic(facts, specs); }, usualMs).checked;
2789
2822
  return v;
2790
2823
  };
2791
2824
  const asTerm = (t) => (t instanceof Term ? t : lit(t));
@@ -0,0 +1,30 @@
1
+ import type { C, E } from './machine.js';
2
+ import { type Lin } from './solver.js';
3
+ import { type Goal, type Path, type Subst, type Tree } from '../kernel/kernel.js';
4
+ export type Rename = {
5
+ s: Subst;
6
+ cells?: Record<string, {
7
+ to: string;
8
+ at: E;
9
+ }>;
10
+ };
11
+ export type Renamer = (lin: Lin, wp: Path) => Lin | undefined;
12
+ export declare function renamer(r: Rename): Renamer;
13
+ export type TStats = {
14
+ leaves: number;
15
+ regrown: number;
16
+ splits: number;
17
+ carried: number;
18
+ uncarried: number;
19
+ cached: number;
20
+ };
21
+ export declare const tstats: () => TStats;
22
+ export declare function translate(part: Goal, whole: Goal, tree: Tree, ren0: Renamer, st: TStats, until: number, pp?: Path, wp?: Path, swaps?: [string, string][]): Tree | undefined;
23
+ export type Reads = {
24
+ k: E;
25
+ w: E;
26
+ }[];
27
+ export declare function readsFirst(whole: Goal, reads: Reads, ren: Renamer, inner: (wp: Path, swaps: [string, string][]) => Tree | undefined, st: TStats, until: number, wp?: Path, swaps?: [string, string][]): Tree | undefined;
28
+ export declare function decide(whole: Goal, conds: C[], inner: (wp: Path) => Tree | undefined, st: TStats, until: number, wp?: Path): Tree | undefined;
29
+ export declare function close(g: Goal, wp: Path, st: TStats, until: number): Tree | undefined;
30
+ export declare function carry(g: Goal, nFacts: number, nUses: number, st: TStats, until: number): Tree | undefined;