gdp-wired 0.16.0 → 0.18.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.
- package/README.md +10 -2
- package/dist/core/cycles.d.ts +73 -0
- package/dist/core/cycles.js +899 -0
- package/dist/core/cyclic.d.ts +67 -0
- package/dist/core/cyclic.js +740 -0
- package/dist/core/data.d.ts +258 -0
- package/dist/core/data.js +1858 -0
- package/dist/core/discover.js +7 -2
- package/dist/core/dsl.d.ts +7 -1
- package/dist/core/dsl.js +39 -6
- package/dist/core/infer.js +5 -1
- package/dist/kernel/cyclic.d.ts +45 -0
- package/dist/kernel/cyclic.js +54 -0
- package/dist/kernel/prove.d.ts +15 -0
- package/dist/kernel/prove.js +485 -5
- package/dist/lean-kernel/emit.d.ts +73 -0
- package/dist/lean-kernel/emit.js +250 -26
- package/dist/lean-kernel/library.js +3 -0
- package/package.json +14 -2
package/dist/core/discover.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// const out = discover(r, laws, stepLaws)
|
|
11
11
|
// out.verdict // the laws, proven of out.rule: r with its functions and lists made again with the lemmas
|
|
12
12
|
// out.found // per function or list, the lemmas found, as source to paste into recur(...) or carriesList(...)
|
|
13
|
+
import { quietly } from "./cyclic.js";
|
|
13
14
|
import { any, atMost, compileFunction, evalC, evalE, n, plus, prod, same, times, x } from "./machine.js";
|
|
14
15
|
import { Unproven, lit, proveFold, proveSteps, recur, recurOf } from "./dsl.js";
|
|
15
16
|
const seeded = (seed) => { let s = seed; return (k) => { s = (s * 1103515245 + 12345) % 2 ** 31; return Math.floor(s / 2 ** 31 * k); }; };
|
|
@@ -260,7 +261,10 @@ function houdiniList(r, part, cands, params) {
|
|
|
260
261
|
// The laws of r, and where they aren't proven as r states them, the lemmas found for its functions and lists: all that
|
|
261
262
|
// is proven first, then each dropped in turn while the laws stay proven without it.
|
|
262
263
|
// `first`: the laws' verdict on r alone, when the caller has it.
|
|
263
|
-
export function discover(r, laws, stepLaws = [], first
|
|
264
|
+
export function discover(r, laws, stepLaws = [], first) {
|
|
265
|
+
return quietly(() => discoverSearch(r, laws, stepLaws, first ?? proveSteps(r, laws, stepLaws, { search: false })));
|
|
266
|
+
}
|
|
267
|
+
function discoverSearch(r, laws, stepLaws, first) {
|
|
264
268
|
if (first.proven && first.checked)
|
|
265
269
|
return { verdict: first, rule: r, found: {} };
|
|
266
270
|
const { params, fns, lists } = r.parts, ps = natParams(params);
|
|
@@ -405,7 +409,8 @@ const eOfSide = (side) => {
|
|
|
405
409
|
return es.length ? es.slice(1).reduce((acc, t) => plus(acc, t), es[0]) : n(0);
|
|
406
410
|
};
|
|
407
411
|
const cOfCand = (c) => { const { pos, neg } = sides(c.lin), a = eOfSide(pos), b = eOfSide(neg); return c.rel === '=' ? same(a, b) : atMost(a, b); };
|
|
408
|
-
export function discoverFold(f, laws) {
|
|
412
|
+
export function discoverFold(f, laws) { return quietly(() => discoverFoldSearch(f, laws)); }
|
|
413
|
+
function discoverFoldSearch(f, laws) {
|
|
409
414
|
const stated = f.lemmas ?? [], first = proveFold({ fold: f.fold, lemmas: stated }, laws);
|
|
410
415
|
if (first.proven && first.checked)
|
|
411
416
|
return { verdict: first, lemmas: [], found: [] };
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -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[]
|
|
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,9 +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 } from "../kernel/prove.js";
|
|
21
|
+
import { factsFor, grow, proveLaws, proveLiveness, proveStepLaws, withDeadline } from "../kernel/prove.js";
|
|
22
|
+
import { crossCyclic, cyclicOn, foldNodes, proveCyclic, quietly, recurNodes } from "./cyclic.js";
|
|
22
23
|
import { discover } from "./discover.js";
|
|
23
24
|
import { inferLaws } from "./infer.js";
|
|
25
|
+
import { cycleLemmas, findRank } from "./cycles.js";
|
|
24
26
|
// --- terms and conditions -------------------------------------------------------------------------------------------
|
|
25
27
|
const naturalConst = (v) => {
|
|
26
28
|
if (!Number.isSafeInteger(v) || v < 0)
|
|
@@ -1399,6 +1401,10 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1399
1401
|
const stated = Object.entries(lemmas).map(([n, f]) => { const holds = f(named('x'), named('fx'), envAt(own)).c; only(`lemma "${n}"`, holds, ['x', 'fx', ...args], uses.map(([u]) => u)); return { name: n, holds }; });
|
|
1400
1402
|
const pairs = Object.entries(between).map(([n, f]) => { const holds = f(named('x'), named('fx'), named('y'), named('fy'), envAt(own)).c; only(`law between "${n}"`, holds, ['x', 'fx', 'y', 'fy', ...args], uses.map(([u]) => u)); return { name: n, holds }; });
|
|
1401
1403
|
const argKinds = Object.fromEntries(args.map(a => [a, nat]));
|
|
1404
|
+
// The recurrence as a cyclic proof's instance (core/cyclic.ts, GDP_CYCLIC=1): the facts of the functions it reads, from
|
|
1405
|
+
// a rule that reads them.
|
|
1406
|
+
const recurCase = (between) => ({ name, args, zero, next, lemmas: stated, between,
|
|
1407
|
+
facts: uses.length ? rule(`${name}'s functions`).fns(def.fns ?? {}).inputs({}).state({ z: 0 }).step((env) => ({ z: env.z })).machine.facts ?? [] : [] });
|
|
1402
1408
|
const failed = (what, v) => { if (!(v.proven && v.checked))
|
|
1403
1409
|
throw new Unproven(`${name}'s ${what}`, v); };
|
|
1404
1410
|
// The lemmas: x counting up from 0 and fx its value, each lemma a law of that machine.
|
|
@@ -1413,7 +1419,11 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1413
1419
|
const r = r0.state({ x: 0, fx: ((p) => (zeroReads ? p.fx0 : zeroAt(args.map(a => p[a])))) })
|
|
1414
1420
|
.step((env) => ({ x: env.x.plus(1), fx: def.next(env.x, env.fx, envAt(args.map(a => env[a]))) }));
|
|
1415
1421
|
const laws = Object.entries(lemmas).map(([n, f]) => r.law(n, (env) => f(env.x, env.fx, envAt(args.map(a => env[a])))));
|
|
1416
|
-
|
|
1422
|
+
// (Its machine's proof timed alone: the recurrence is compared below as its own instance.)
|
|
1423
|
+
const t0 = performance.now();
|
|
1424
|
+
verdict = quietly(() => proveRule(r, laws, { search: false }));
|
|
1425
|
+
if (cyclicOn())
|
|
1426
|
+
crossCyclic('recurrence', name, verdict.proven && verdict.checked, () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0);
|
|
1417
1427
|
failed('lemmas', verdict);
|
|
1418
1428
|
proofs.lemmas = { machine: r.machine, laws };
|
|
1419
1429
|
}
|
|
@@ -1450,7 +1460,9 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1450
1460
|
.step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(at(env))) }));
|
|
1451
1461
|
const laws = [...Object.entries(lemmas).map(([n, l]) => r.law(`${n} at y`, (env) => l(env.y, env.fy, envAt(at(env))))),
|
|
1452
1462
|
...Object.entries(between).map(([n, l]) => r.law(n, (env) => l(env.x, env.fx, env.y, env.fy, envAt(at(env)))))];
|
|
1453
|
-
const v = proveRule(r, laws, { search: false });
|
|
1463
|
+
const t0 = performance.now(), v = quietly(() => proveRule(r, laws, { search: false })), usualMs = performance.now() - t0;
|
|
1464
|
+
if (cyclicOn())
|
|
1465
|
+
crossCyclic('recurrence, between', name, v.proven && v.checked, () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs);
|
|
1454
1466
|
failed('laws between two points', v);
|
|
1455
1467
|
proofs.between = { machine: r.machine, laws };
|
|
1456
1468
|
verdict = { proven: true, checked: verdict.checked && v.checked };
|
|
@@ -2472,6 +2484,7 @@ export class LoopRule {
|
|
|
2472
2484
|
...(assume.length ? { assume: assume.length === 1 ? assume[0] : { op: 'and', xs: assume } } : {}),
|
|
2473
2485
|
};
|
|
2474
2486
|
}
|
|
2487
|
+
get hasRank() { return !!this.spec.rank; }
|
|
2475
2488
|
get rankTerms() {
|
|
2476
2489
|
if (!this.spec.rank)
|
|
2477
2490
|
throw new Error(`${this.spec.name}: no rank`);
|
|
@@ -2607,12 +2620,19 @@ function searched(r, laws, stepLaws, v, opts) {
|
|
|
2607
2620
|
let rule = r, lemmas;
|
|
2608
2621
|
const { fns, lists } = r.parts;
|
|
2609
2622
|
if (lists.length || fns.some(([, d]) => recurOf(d))) {
|
|
2610
|
-
const d = discover(r, laws, stepLaws, v);
|
|
2623
|
+
const d = withDeadline(until, () => discover(r, laws, stepLaws, v));
|
|
2611
2624
|
if (d.verdict.proven && d.verdict.checked)
|
|
2612
2625
|
return { ...d.verdict, found: { lemmas: d.found } };
|
|
2613
2626
|
if (Object.keys(d.found).length)
|
|
2614
2627
|
(rule = d.rule, lemmas = d.found);
|
|
2615
2628
|
}
|
|
2629
|
+
// Functions known by their facts alone (not a recurrence): lemmas about them, fitted to their values and proven from
|
|
2630
|
+
// the facts as cyclic claims, the laws proven with them as facts.
|
|
2631
|
+
if ((typeof opts.search !== 'object' || opts.search.cycles !== false) && fns.some(([, d]) => !recurOf(d)) && rule.machine.facts?.length && until - performance.now() > 500) {
|
|
2632
|
+
const c = withDeadline(until, () => cycleLemmas(rule.machine, withKinds(rule, laws), stepLaws, { ms: (until - performance.now()) / 2 }));
|
|
2633
|
+
if (c.proven && c.checked)
|
|
2634
|
+
return { proven: true, checked: true, found: { ...(lemmas ? { lemmas } : {}), facts: c.found.map(f => f.text) } };
|
|
2635
|
+
}
|
|
2616
2636
|
const ms = until - performance.now();
|
|
2617
2637
|
if (ms < 500)
|
|
2618
2638
|
return v;
|
|
@@ -2656,7 +2676,15 @@ export function crossCheckDecision(d, opts = {}) {
|
|
|
2656
2676
|
}
|
|
2657
2677
|
return { checked };
|
|
2658
2678
|
}
|
|
2659
|
-
|
|
2679
|
+
// With no rank stated, the rank is searched for from the loop's runs (core/cycles.ts's findRank), with the invariants it
|
|
2680
|
+
// needs (inferLaws), within `ms` (10 s); the verdict is the kernel's on the rank and the laws found, in `found`.
|
|
2681
|
+
export const proveLoop = (l, laws, opts = {}) => {
|
|
2682
|
+
if (l.hasRank)
|
|
2683
|
+
return goalsVerdict(loopObligations(l.loop, laws, l.rankTerms));
|
|
2684
|
+
const out = findRank(l.loop, laws, opts);
|
|
2685
|
+
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 } : {}),
|
|
2686
|
+
...(out.found ? { found: { rank: out.found.rank, ...(out.laws?.length ? { laws: out.laws.map((law, i) => ({ law, text: out.found.laws[i] ?? law.name })) } : {}) } } : {}) };
|
|
2687
|
+
};
|
|
2660
2688
|
export class FoldRule {
|
|
2661
2689
|
spec;
|
|
2662
2690
|
constructor(spec) {
|
|
@@ -2771,7 +2799,12 @@ export class FoldRule {
|
|
|
2771
2799
|
}
|
|
2772
2800
|
export const fold = (name) => new FoldRule({ name, params: [], inputs: [], pre: [], assume: [] });
|
|
2773
2801
|
// A fold's laws proven over every tree (a rose fold's own laws, its children's lemmas, with them).
|
|
2774
|
-
export const proveFold = (f, laws) =>
|
|
2802
|
+
export const proveFold = (f, laws) => {
|
|
2803
|
+
const all = [...(f.lemmas ?? []), ...laws], t0 = performance.now(), v = goalsVerdict(foldObligations(f.fold, all)), usualMs = performance.now() - t0;
|
|
2804
|
+
if (cyclicOn())
|
|
2805
|
+
crossCyclic('fold', f.fold.name, v.proven && v.checked, () => { const { facts, specs } = foldNodes(f.fold, all); return proveCyclic(facts, specs); }, usualMs);
|
|
2806
|
+
return v;
|
|
2807
|
+
};
|
|
2775
2808
|
const asTerm = (t) => (t instanceof Term ? t : lit(t));
|
|
2776
2809
|
// A fold over trees whose nodes have any number of children, as the kernel's binary fold (so the kernel and Lean's
|
|
2777
2810
|
// fold_sound check it as they check any fold): a node's children become a chain of glue nodes, each folding one child
|
package/dist/core/infer.js
CHANGED
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
// its witness is a counterexample to induction, so the candidate that rules it out (and the most of those seen) is
|
|
14
14
|
// added; a chosen candidate that nothing supports, or that makes a proof slow, is dropped; the set proven as a whole
|
|
15
15
|
// is the answer, after each chosen candidate the proof doesn't need is taken out again
|
|
16
|
+
import { quietly } from "./cyclic.js";
|
|
16
17
|
import { any, atMost, evalC, get, interpret, n, not, plus, same, times, x } from "./machine.js";
|
|
17
18
|
import { proveRule, proveSteps } from "./dsl.js";
|
|
18
19
|
import { allowed, watch } from "./watch.js";
|
|
19
|
-
import { proveLaw, proveLaws, proveStepLaws } from "../kernel/prove.js";
|
|
20
|
+
import { proveLaw, proveLaws, proveStepLaws, withDeadline } from "../kernel/prove.js";
|
|
20
21
|
const KEY = 'key$found', AT = '@', K = '#';
|
|
21
22
|
const seeded = (seed) => { let s = seed; return k => { s = (s * 1103515245 + 12345) % 2 ** 31; return Math.floor(s / 2 ** 31 * k); }; };
|
|
22
23
|
const isRule = (t) => typeof t.kindsOfState === 'function';
|
|
@@ -106,6 +107,9 @@ const keyValue = (cell, w) => {
|
|
|
106
107
|
return value;
|
|
107
108
|
};
|
|
108
109
|
export function inferLaws(target, laws, stepLaws = [], opts = {}) {
|
|
110
|
+
return quietly(() => withDeadline(performance.now() + (opts.ms ?? 20000), () => inferLawsSearch(target, laws, stepLaws, opts)));
|
|
111
|
+
}
|
|
112
|
+
function inferLawsSearch(target, laws, stepLaws = [], opts = {}) {
|
|
109
113
|
const t0 = performance.now(), until = t0 + (opts.ms ?? 20000), rule = isRule(target) ? target : undefined;
|
|
110
114
|
const m = rule ? rule.machine : target;
|
|
111
115
|
let proofs = 0;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { C, E } from '../core/machine.js';
|
|
2
|
+
import { type Goal, type Tree } from './kernel.js';
|
|
3
|
+
export type Fact = {
|
|
4
|
+
name: string;
|
|
5
|
+
keys: string[];
|
|
6
|
+
holds: C;
|
|
7
|
+
};
|
|
8
|
+
export type Use = {
|
|
9
|
+
node: number;
|
|
10
|
+
subst: Record<string, E>;
|
|
11
|
+
guard: C;
|
|
12
|
+
premises: Tree[];
|
|
13
|
+
descent: Tree;
|
|
14
|
+
};
|
|
15
|
+
export type CNode = {
|
|
16
|
+
name: string;
|
|
17
|
+
hyps: C[];
|
|
18
|
+
concl: C;
|
|
19
|
+
rank: E[];
|
|
20
|
+
insts: {
|
|
21
|
+
fact: number;
|
|
22
|
+
at: E[];
|
|
23
|
+
}[];
|
|
24
|
+
uses: Use[];
|
|
25
|
+
main: Tree;
|
|
26
|
+
};
|
|
27
|
+
export type Cyclic = {
|
|
28
|
+
facts: Fact[];
|
|
29
|
+
nodes: CNode[];
|
|
30
|
+
};
|
|
31
|
+
export declare const falseC: C;
|
|
32
|
+
export declare const lexLt: (as: E[], bs: E[]) => C;
|
|
33
|
+
export declare const instHyps: (facts: Fact[], insts: CNode["insts"]) => C[];
|
|
34
|
+
export declare const baseHyps: (facts: Fact[], n: Pick<CNode, "hyps" | "insts">) => C[];
|
|
35
|
+
export declare const useHyps: (nodes: Pick<CNode, "concl">[], uses: Pick<Use, "node" | "subst" | "guard">[]) => C[];
|
|
36
|
+
type Shape = Pick<CNode, 'name' | 'hyps' | 'concl' | 'rank' | 'insts'> & {
|
|
37
|
+
uses: Pick<Use, 'node' | 'subst' | 'guard'>[];
|
|
38
|
+
};
|
|
39
|
+
export declare const mainGoal: (facts: Fact[], nodes: Shape[], n: Shape) => Goal;
|
|
40
|
+
export declare const premiseGoal: (facts: Fact[], n: Shape, u: Shape["uses"][number], c: C, i: number) => Goal;
|
|
41
|
+
export declare const descentGoal: (facts: Fact[], n: Shape, u: Shape["uses"][number], m: Shape) => Goal;
|
|
42
|
+
export declare const factsAvoid: (facts: Fact[], subst: Record<string, E>) => boolean;
|
|
43
|
+
export declare function refuseCyclic(p: Cyclic): string | undefined;
|
|
44
|
+
export declare const checkCyclic: (p: Cyclic) => boolean;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { check, substC, substE } from "./kernel.js";
|
|
2
|
+
const zero = { op: 'const', value: 0 };
|
|
3
|
+
export const falseC = { op: '<', a: zero, b: zero };
|
|
4
|
+
// as below bs in order: the first place they differ, as's is smaller (Lean's lexLtC, built the same way).
|
|
5
|
+
export const lexLt = (as, bs) => (as.length && bs.length
|
|
6
|
+
? { op: 'or', xs: [{ op: '<', a: as[0], b: bs[0] }, { op: 'and', xs: [{ op: '=', a: as[0], b: bs[0] }, lexLt(as.slice(1), bs.slice(1))] }] }
|
|
7
|
+
: falseC);
|
|
8
|
+
const sub = (s) => ({ vars: s, maps: {}, flags: {} });
|
|
9
|
+
// A fact at the terms a node names: its keys paired with the terms (the shorter list decides, as Lean's zip does).
|
|
10
|
+
const keyed = (keys, at) => Object.fromEntries(keys.slice(0, at.length).map((key, i) => [key, at[i]]));
|
|
11
|
+
export const instHyps = (facts, insts) => insts.flatMap(p => { const f = facts[p.fact]; return f ? [substC(f.holds, sub(keyed(f.keys, p.at)))] : []; });
|
|
12
|
+
export const baseHyps = (facts, n) => [...n.hyps, ...instHyps(facts, n.insts)];
|
|
13
|
+
export const useHyps = (nodes, uses) => uses.flatMap(u => {
|
|
14
|
+
const m = nodes[u.node];
|
|
15
|
+
return m ? [{ op: 'or', xs: [{ op: 'not', x: u.guard }, substC(m.concl, sub(u.subst))] }] : [];
|
|
16
|
+
});
|
|
17
|
+
export const mainGoal = (facts, nodes, n) => ({ name: `${n.name}`, hyps: [...baseHyps(facts, n), ...useHyps(nodes, n.uses), { op: 'not', x: n.concl }] });
|
|
18
|
+
export const premiseGoal = (facts, n, u, c, i) => ({ name: `${n.name}: use of node ${u.node}, its hypothesis ${i}`, hyps: [...baseHyps(facts, n), u.guard, { op: 'not', x: substC(c, sub(u.subst)) }] });
|
|
19
|
+
export const descentGoal = (facts, n, u, m) => ({ name: `${n.name}: use of node ${u.node}, its rank below`, hyps: [...baseHyps(facts, n), u.guard, { op: 'not', x: lexLt(m.rank.map(e => substE(e, sub(u.subst))), n.rank) }] });
|
|
20
|
+
const occurs = (name, t) => JSON.stringify(t).includes(`"op":"var","name":${JSON.stringify(name)}`);
|
|
21
|
+
// The language Lean's twin has: no flag, no quantifier.
|
|
22
|
+
const plain = (t) => !/"op":"(?:flag|all)"/.test(JSON.stringify(t));
|
|
23
|
+
export const factsAvoid = (facts, subst) => Object.keys(subst).every(v => facts.every(f => f.keys.includes(v) || !occurs(v, f.holds)));
|
|
24
|
+
// Why a cyclic proof is refused, or undefined when it checks.
|
|
25
|
+
export function refuseCyclic(p) {
|
|
26
|
+
const { facts, nodes } = p;
|
|
27
|
+
if (!plain(facts) || !plain(nodes.map(n => [n.hyps, n.concl, n.rank, n.insts, n.uses.map(u => [u.subst, u.guard])])))
|
|
28
|
+
return 'a flag or a quantifier (no cyclic proof reads one)';
|
|
29
|
+
for (const f of facts)
|
|
30
|
+
if (new Set(f.keys).size !== f.keys.length)
|
|
31
|
+
return `fact ${f.name}: a key twice`;
|
|
32
|
+
for (const n of nodes) {
|
|
33
|
+
if (!check(mainGoal(facts, nodes, n), n.main))
|
|
34
|
+
return `${n.name}: its goal`;
|
|
35
|
+
for (const [j, u] of n.uses.entries()) {
|
|
36
|
+
const m = nodes[u.node];
|
|
37
|
+
if (!m)
|
|
38
|
+
return `${n.name}: use ${j} names no node`;
|
|
39
|
+
if (m.rank.length !== n.rank.length)
|
|
40
|
+
return `${n.name}: use ${j} compares ranks of different lengths`;
|
|
41
|
+
if (!factsAvoid(facts, u.subst))
|
|
42
|
+
return `${n.name}: use ${j} assigns a variable a fact reads`;
|
|
43
|
+
if (m.hyps.length !== u.premises.length)
|
|
44
|
+
return `${n.name}: use ${j} has ${u.premises.length} premise trees for ${m.hyps.length} hypotheses`;
|
|
45
|
+
for (const [i, c] of m.hyps.entries())
|
|
46
|
+
if (!check(premiseGoal(facts, n, u, c, i), u.premises[i]))
|
|
47
|
+
return `${n.name}: use ${j}, hypothesis ${i} of ${m.name}`;
|
|
48
|
+
if (!check(descentGoal(facts, n, u, m), u.descent))
|
|
49
|
+
return `${n.name}: use ${j} doesn't lower the rank`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
export const checkCyclic = (p) => refuseCyclic(p) === undefined;
|
package/dist/kernel/prove.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { C, E, Law, Machine } from '../core/machine.js';
|
|
2
2
|
import { type Goal, type Instances, type Liveness, type Path, type StepLaw, type Tree } from './kernel.js';
|
|
3
|
+
export declare const pairedKeys: {
|
|
4
|
+
skip: boolean;
|
|
5
|
+
};
|
|
3
6
|
export declare const profile: {
|
|
4
7
|
on: boolean;
|
|
5
8
|
goals: {
|
|
@@ -21,6 +24,11 @@ export declare const solveKinds: Record<string, {
|
|
|
21
24
|
ms: number;
|
|
22
25
|
rows: number;
|
|
23
26
|
}>;
|
|
27
|
+
export declare let proveDump: ((goal: Goal) => void) | undefined;
|
|
28
|
+
export declare const setProveDump: (f: ((goal: Goal) => void) | undefined) => void;
|
|
29
|
+
export declare const cellProducts: {
|
|
30
|
+
on: boolean;
|
|
31
|
+
};
|
|
24
32
|
export type Outcome = {
|
|
25
33
|
tree: Tree;
|
|
26
34
|
nodes: number;
|
|
@@ -30,10 +38,17 @@ export type Outcome = {
|
|
|
30
38
|
path: Path;
|
|
31
39
|
why?: string;
|
|
32
40
|
};
|
|
41
|
+
export declare function withDeadline<T>(until: number, f: () => T): T;
|
|
33
42
|
export declare function grow(goal: Goal, path?: Path, budget?: {
|
|
34
43
|
nodes: number;
|
|
35
44
|
until?: number;
|
|
45
|
+
units?: boolean;
|
|
36
46
|
}): Outcome;
|
|
47
|
+
export declare function widen(part: Goal, whole: Goal, tree: Tree, opaque?: Record<string, E>, pp?: Path, wp?: Path): Tree | undefined;
|
|
48
|
+
export declare const minimal: {
|
|
49
|
+
on: boolean;
|
|
50
|
+
};
|
|
51
|
+
export declare function usedHyps(goal: Goal, tree: Tree, path?: Path, out?: Set<number>): Set<number>;
|
|
37
52
|
export type Report = {
|
|
38
53
|
proven: boolean;
|
|
39
54
|
nodes: number;
|