gdp-wired 0.17.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 -3
- package/dist/core/cycles.d.ts +73 -0
- package/dist/core/cycles.js +899 -0
- package/dist/core/cyclic.d.ts +18 -2
- package/dist/core/cyclic.js +488 -43
- package/dist/core/data.d.ts +258 -0
- package/dist/core/data.js +1858 -0
- package/dist/core/dsl.d.ts +7 -1
- package/dist/core/dsl.js +18 -1
- package/dist/kernel/prove.d.ts +5 -0
- package/dist/kernel/prove.js +160 -5
- package/dist/lean-kernel/emit.d.ts +61 -1
- package/dist/lean-kernel/emit.js +211 -35
- package/dist/lean-kernel/library.js +2 -0
- package/package.json +10 -2
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
|
@@ -22,6 +22,7 @@ import { factsFor, grow, proveLaws, proveLiveness, proveStepLaws, withDeadline }
|
|
|
22
22
|
import { crossCyclic, cyclicOn, foldNodes, proveCyclic, quietly, recurNodes } 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)
|
|
@@ -2483,6 +2484,7 @@ export class LoopRule {
|
|
|
2483
2484
|
...(assume.length ? { assume: assume.length === 1 ? assume[0] : { op: 'and', xs: assume } } : {}),
|
|
2484
2485
|
};
|
|
2485
2486
|
}
|
|
2487
|
+
get hasRank() { return !!this.spec.rank; }
|
|
2486
2488
|
get rankTerms() {
|
|
2487
2489
|
if (!this.spec.rank)
|
|
2488
2490
|
throw new Error(`${this.spec.name}: no rank`);
|
|
@@ -2624,6 +2626,13 @@ function searched(r, laws, stepLaws, v, opts) {
|
|
|
2624
2626
|
if (Object.keys(d.found).length)
|
|
2625
2627
|
(rule = d.rule, lemmas = d.found);
|
|
2626
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
|
+
}
|
|
2627
2636
|
const ms = until - performance.now();
|
|
2628
2637
|
if (ms < 500)
|
|
2629
2638
|
return v;
|
|
@@ -2667,7 +2676,15 @@ export function crossCheckDecision(d, opts = {}) {
|
|
|
2667
2676
|
}
|
|
2668
2677
|
return { checked };
|
|
2669
2678
|
}
|
|
2670
|
-
|
|
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
|
+
};
|
|
2671
2688
|
export class FoldRule {
|
|
2672
2689
|
spec;
|
|
2673
2690
|
constructor(spec) {
|
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: {
|
|
@@ -41,9 +44,11 @@ export declare function grow(goal: Goal, path?: Path, budget?: {
|
|
|
41
44
|
until?: number;
|
|
42
45
|
units?: boolean;
|
|
43
46
|
}): Outcome;
|
|
47
|
+
export declare function widen(part: Goal, whole: Goal, tree: Tree, opaque?: Record<string, E>, pp?: Path, wp?: Path): Tree | undefined;
|
|
44
48
|
export declare const minimal: {
|
|
45
49
|
on: boolean;
|
|
46
50
|
};
|
|
51
|
+
export declare function usedHyps(goal: Goal, tree: Tree, path?: Path, out?: Set<number>): Set<number>;
|
|
47
52
|
export type Report = {
|
|
48
53
|
proven: boolean;
|
|
49
54
|
nodes: number;
|
package/dist/kernel/prove.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { add, k, refuteFast, scale, solve, sub, v } from "../core/solver.js";
|
|
2
|
+
import { checkRefutation } from "../core/check.js";
|
|
2
3
|
import { crossCyclic, cyclicMachine, cyclicOn, machineNodes, proveCyclic } from "../core/cyclic.js";
|
|
3
4
|
import { assumed, atomOf, boundOf, canon, canonFlip, columnTerm, decided, factorsOf, isCell, isMonomial, stepZone, check, checkLaws, checkStepLaws, historyMachine, leafConstraints, leafConstraintsReusing, reusing, linear, livenessHelpful, livenessParts, obligations, plainKey, progressObligations, stepLawObligations, trueC, truth, unreachable, witnessesOf, withProducts } from "./kernel.js";
|
|
4
5
|
// The atoms the path leaves open, in evaluation order: deciding any of them makes progress.
|
|
@@ -77,7 +78,10 @@ function openH(c, path, out, positive = true) {
|
|
|
77
78
|
if (out.length === before && !positive && c.op === '=' && truth(c, path) === undefined)
|
|
78
79
|
openC(c, path, out);
|
|
79
80
|
}
|
|
80
|
-
// Pairs of reads of one map whose keys the path doesn't yet compare.
|
|
81
|
+
// Pairs of reads of one map whose keys the path doesn't yet compare. With `pairedKeys.skip` (core/data.ts), reads at
|
|
82
|
+
// keys a pairing builds ((a + b)² + b, keys whose difference has monomials) are left uncompared: that encoding states
|
|
83
|
+
// their congruence as facts, and comparing their keys only brings products into leaves its linear rows decide.
|
|
84
|
+
export const pairedKeys = { skip: false };
|
|
81
85
|
function openCells(rows, path) {
|
|
82
86
|
const names = [...new Set(rows.flatMap(r => Object.keys(r.lin)))].filter(isCell);
|
|
83
87
|
const out = [];
|
|
@@ -87,7 +91,27 @@ function openCells(rows, path) {
|
|
|
87
91
|
if (p.slice(0, p.indexOf('[')) !== q.slice(0, q.indexOf('[')))
|
|
88
92
|
continue;
|
|
89
93
|
const d = sub(parse(keyText(p)), parse(keyText(q)));
|
|
94
|
+
if (pairedKeys.skip && Object.keys(d).some(isMonomial))
|
|
95
|
+
continue;
|
|
90
96
|
const dec = (lin) => path.get(canon(lin))?.holds ?? (path.has(canonFlip(lin)) ? !path.get(canonFlip(lin)).holds : undefined);
|
|
97
|
+
// Two cells of a run's map (core/cyclic.ts: time u and key k at u + k·Ω) at keys k·Ω and k'·Ω: k = k' first, a
|
|
98
|
+
// linear atom; the keys' own difference only where k = k' (its other side then refuted by (k − k')·Ω = 0, a product).
|
|
99
|
+
const q0 = cellProducts.on ? quotient(d) : undefined;
|
|
100
|
+
if (q0) {
|
|
101
|
+
const qa = dec(q0), qb = dec(scale(q0, -1));
|
|
102
|
+
if (qa === undefined) {
|
|
103
|
+
out.push(q0);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (qa === true && qb === undefined) {
|
|
107
|
+
out.push(scale(q0, -1));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (!(qa && qb))
|
|
111
|
+
continue;
|
|
112
|
+
cellDiffs.set(canon(d), q0);
|
|
113
|
+
cellDiffs.set(canon(scale(d, -1)), scale(q0, -1));
|
|
114
|
+
}
|
|
91
115
|
const a = dec(d), b = dec(scale(d, -1));
|
|
92
116
|
if (a === undefined)
|
|
93
117
|
out.push(d);
|
|
@@ -96,6 +120,24 @@ function openCells(rows, path) {
|
|
|
96
120
|
}
|
|
97
121
|
return out;
|
|
98
122
|
}
|
|
123
|
+
// A key difference q·F over monomials that share one factor F (Ω), with no other part: q, else undefined.
|
|
124
|
+
const quotient = (d) => {
|
|
125
|
+
const keys = Object.keys(d);
|
|
126
|
+
if (!keys.length || keys.some(key => key === '1' || !isMonomial(key)))
|
|
127
|
+
return undefined;
|
|
128
|
+
const shared = factorsOf(keys[0]).find(f => keys.every(key => factorsOf(key).includes(f)));
|
|
129
|
+
if (!shared)
|
|
130
|
+
return undefined;
|
|
131
|
+
const out = {};
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
const fs = [...factorsOf(key)];
|
|
134
|
+
fs.splice(fs.indexOf(shared), 1);
|
|
135
|
+
out[fs.join('*')] = (out[fs.join('*')] ?? 0) + d[key];
|
|
136
|
+
}
|
|
137
|
+
return out;
|
|
138
|
+
};
|
|
139
|
+
// The cells' key differences split at their quotient's equality, by canonical text: their quotient.
|
|
140
|
+
const cellDiffs = new Map();
|
|
99
141
|
function parse(text) {
|
|
100
142
|
const out = {};
|
|
101
143
|
let depth = 0, start = 0;
|
|
@@ -225,7 +267,7 @@ function growPlain(goal, path, budget) {
|
|
|
225
267
|
return { stuck: 'unknown', path, why: outOfBudget(goal, budget.nodes > 200_000 ? 'nodes' : 'time', path.size) };
|
|
226
268
|
// Rows that read a term column only once a cut has put one on the path; before, the rows a choice-free search uses
|
|
227
269
|
// (what it saw before term columns), renumbered to the whole leaf's when a leaf is found.
|
|
228
|
-
const full = timed(() =>
|
|
270
|
+
const full = timed(() => rowsAt(goal, path), ms => { profile.rowsMs += ms; });
|
|
229
271
|
const columns = [...path.values()].some(a => Object.keys(a.lin).some(key => key.startsWith('⟦')));
|
|
230
272
|
const idx = full.flatMap((r, i) => (columns || !Object.keys(r.lin).some(key => key.startsWith('⟦')) ? [i] : []));
|
|
231
273
|
const rows = idx.map(i => full[i]), at = (i) => (i < rows.length ? idx[i] : full.length + i - rows.length);
|
|
@@ -278,6 +320,21 @@ function growPlain(goal, path, budget) {
|
|
|
278
320
|
return { tree: { leaf: fast.steps }, nodes: budget.nodes };
|
|
279
321
|
}
|
|
280
322
|
let split = next ?? (verdict.kind === 'unknown' ? bisect(rows, path) ?? fractional(verdict.point, path) : undefined);
|
|
323
|
+
// A cell difference's failing side (k = k' on the path, the keys apart): (k − k')·Ω = 0 refutes it, a product of each
|
|
324
|
+
// of the path's two atoms k − k' <= 0 and k' − k <= 0 with Ω >= 0.
|
|
325
|
+
if (cellProducts.on && path.size) {
|
|
326
|
+
const [id, last] = [...path].at(-1), q0 = !last.holds ? cellDiffs.get(id) : undefined;
|
|
327
|
+
if (q0) {
|
|
328
|
+
const at = (lin) => rows.findIndex(r => canon(r.lin) === canon(lin));
|
|
329
|
+
const d = last.lin, f = factorsOf(Object.keys(d)[0]).find(x => !Object.keys(q0).includes(x) && Object.keys(d).every(key => factorsOf(key).includes(x)));
|
|
330
|
+
const e1 = at(q0), e2 = at(scale(q0, -1)), sign = f === undefined ? -1 : at({ [f]: -1 });
|
|
331
|
+
if (e1 >= 0 && e2 >= 0 && sign >= 0) {
|
|
332
|
+
const pairs = [[e1, sign], [e2, sign]], out = solve(withProducts(rows, pairs));
|
|
333
|
+
if (out.kind === 'refuted')
|
|
334
|
+
return { tree: leafOf(out.steps, pairs), nodes: budget.nodes };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
281
338
|
// Products: with monomials about, try every pair of rows multiplied; failing that, split a factor at zero.
|
|
282
339
|
const monomials = rows.some(r => Object.keys(r.lin).some(isMonomial));
|
|
283
340
|
// A witness that multiplies out (each monomial's value is its factors' product) is a real model: nothing to refute.
|
|
@@ -500,6 +557,24 @@ function squareForms(rows) {
|
|
|
500
557
|
// the decision's own row, and any row only it makes, are not there), every split kept. The derivations are the same
|
|
501
558
|
// arithmetic over the same rows, and the kernel checks the whole tree in the end. Undefined at the first leaf that
|
|
502
559
|
// needs the decision.
|
|
560
|
+
// A leaf's rows, by its path's decisions in order (what they are a function of): a lift reads every leaf of a subtree
|
|
561
|
+
// at its own path, found when it was grown, and at the path without the split, which the next lift up reads again.
|
|
562
|
+
const rowsSeen = new WeakMap();
|
|
563
|
+
function rowsAt(goal, path) {
|
|
564
|
+
let byPath = rowsSeen.get(goal);
|
|
565
|
+
if (!byPath)
|
|
566
|
+
rowsSeen.set(goal, (byPath = new Map()));
|
|
567
|
+
let id = '';
|
|
568
|
+
for (const [key, a] of path)
|
|
569
|
+
id += `${a.holds ? '+' : '-'}${key}|`;
|
|
570
|
+
let rows = byPath.get(id);
|
|
571
|
+
if (!rows) {
|
|
572
|
+
if (byPath.size >= 4096)
|
|
573
|
+
byPath.clear();
|
|
574
|
+
byPath.set(id, (rows = leafConstraintsReusing(goal, path)));
|
|
575
|
+
}
|
|
576
|
+
return rows;
|
|
577
|
+
}
|
|
503
578
|
function lift(goal, tree, from, to) {
|
|
504
579
|
const own = to.size; // the decision's row: the last of `from`'s path rows, below every later split's
|
|
505
580
|
const reads = (t) => ('leaf' in t ? t.leaf.some(st => st.kind === 'input' && st.index === own) || (t.products ?? []).some(([i, j]) => i === own || j === own) : reads(t.yes) || reads(t.no));
|
|
@@ -514,7 +589,7 @@ function lift(goal, tree, from, to) {
|
|
|
514
589
|
const no = yes && go(t.no, new Map(a).set(sid, { lin: t.split, holds: false }), new Map(b).set(sid, { lin: t.split, holds: false }));
|
|
515
590
|
return yes && no ? { split: t.split, yes, no } : undefined;
|
|
516
591
|
}
|
|
517
|
-
const ra =
|
|
592
|
+
const ra = rowsAt(goal, a), rb = rowsAt(goal, b), at = new Map();
|
|
518
593
|
rb.forEach((r, i) => { const key = canon(r.lin); if (!at.has(key))
|
|
519
594
|
at.set(key, i); });
|
|
520
595
|
const map = (i) => (i < ra.length ? at.get(canon(ra[i].lin)) : undefined);
|
|
@@ -540,6 +615,86 @@ function lift(goal, tree, from, to) {
|
|
|
540
615
|
};
|
|
541
616
|
return go(tree, from, to);
|
|
542
617
|
}
|
|
618
|
+
// A tree for a weaker goal (some hypotheses left out, some conjuncts of one left out) made a tree for the whole: at any
|
|
619
|
+
// path a leaf's rows are among the whole goal's (each hypothesis' facts are a hypothesis' own, the cells, terms and
|
|
620
|
+
// naturals they read a subset), so each input is renumbered to the same row there, a square or product past the rows
|
|
621
|
+
// moved by the difference. Where a dropped conjunct makes the whole hypothesis false under the path, its row 1 <= 0
|
|
622
|
+
// refutes the leaf alone; a leaf neither maps is grown again on the whole goal.
|
|
623
|
+
// `opaque`: variables the weaker goal has for terms of the whole (a step's value, read the same in two hypotheses), each
|
|
624
|
+
// read as the term's linear form at the whole goal's path; a split the whole's path already decides is followed, not
|
|
625
|
+
// made again.
|
|
626
|
+
export function widen(part, whole, tree, opaque = {}, pp = new Map(), wp = pp) {
|
|
627
|
+
const names = Object.keys(opaque);
|
|
628
|
+
const rename = (lin) => {
|
|
629
|
+
if (!names.length)
|
|
630
|
+
return lin;
|
|
631
|
+
let out = {};
|
|
632
|
+
for (const [key, c] of Object.entries(lin)) {
|
|
633
|
+
const e = opaque[key];
|
|
634
|
+
if (e) {
|
|
635
|
+
const l = linear(e, wp);
|
|
636
|
+
if (!l)
|
|
637
|
+
return undefined;
|
|
638
|
+
out = add(out, scale(l, c));
|
|
639
|
+
}
|
|
640
|
+
else if (names.some(n => key.includes(n)))
|
|
641
|
+
return undefined;
|
|
642
|
+
else
|
|
643
|
+
out = add(out, { [key]: c });
|
|
644
|
+
}
|
|
645
|
+
return out;
|
|
646
|
+
};
|
|
647
|
+
if (!('leaf' in tree)) {
|
|
648
|
+
const id = canon(tree.split), wl = rename(tree.split);
|
|
649
|
+
if (!wl) {
|
|
650
|
+
const o = grow(whole, wp);
|
|
651
|
+
return 'tree' in o ? o.tree : undefined;
|
|
652
|
+
}
|
|
653
|
+
const wid = canon(wl), known = Object.keys(wl).every(key => key === '1') ? (wl['1'] ?? 0) <= 0 : wp.has(wid) ? wp.get(wid).holds : wp.has(canonFlip(wl)) ? !wp.get(canonFlip(wl)).holds : undefined;
|
|
654
|
+
if (known !== undefined)
|
|
655
|
+
return widen(part, whole, known ? tree.yes : tree.no, opaque, new Map(pp).set(id, { lin: tree.split, holds: known }), wp);
|
|
656
|
+
const yes = widen(part, whole, tree.yes, opaque, new Map(pp).set(id, { lin: tree.split, holds: true }), new Map(wp).set(wid, { lin: wl, holds: true }));
|
|
657
|
+
const no = yes && widen(part, whole, tree.no, opaque, new Map(pp).set(id, { lin: tree.split, holds: false }), new Map(wp).set(wid, { lin: wl, holds: false }));
|
|
658
|
+
return yes && no ? { split: wl, yes, no } : undefined;
|
|
659
|
+
}
|
|
660
|
+
const rp = rowsAt(part, pp), rw = rowsAt(whole, wp), at = new Map();
|
|
661
|
+
rw.forEach((r, i) => { const key = canon(r.lin); if (!at.has(key))
|
|
662
|
+
at.set(key, i); });
|
|
663
|
+
const map = (i) => { if (i >= rp.length)
|
|
664
|
+
return i - rp.length + rw.length; const l = rename(rp[i].lin); return l && at.get(canon(l)); };
|
|
665
|
+
const leaf = [], products = [], squares = (tree.squares ?? []).map(rename);
|
|
666
|
+
let ok = squares.every(Boolean);
|
|
667
|
+
for (const st of ok ? tree.leaf : []) {
|
|
668
|
+
if (st.kind !== 'input') {
|
|
669
|
+
leaf.push(st);
|
|
670
|
+
continue;
|
|
671
|
+
}
|
|
672
|
+
const j = map(st.index);
|
|
673
|
+
if (j === undefined) {
|
|
674
|
+
ok = false;
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
leaf.push({ ...st, index: j });
|
|
678
|
+
}
|
|
679
|
+
for (const [i, j] of ok ? tree.products ?? [] : []) {
|
|
680
|
+
const pi = map(i), pj = map(j);
|
|
681
|
+
if (pi === undefined || pj === undefined) {
|
|
682
|
+
ok = false;
|
|
683
|
+
break;
|
|
684
|
+
}
|
|
685
|
+
products.push([pi, pj]);
|
|
686
|
+
}
|
|
687
|
+
// (renamed, a derivation through products or cuts is replayed: a cut's divisor may not divide the terms' coefficients)
|
|
688
|
+
if (ok && names.length && !checkRefutation(withProducts(rw, products, squares), leaf))
|
|
689
|
+
ok = false;
|
|
690
|
+
if (ok)
|
|
691
|
+
return { leaf, ...(tree.products ? { products } : {}), ...(tree.squares ? { squares: squares } : {}) };
|
|
692
|
+
const absurd = rw.findIndex(r => Object.keys(r.lin).every(key => key === '1') && (r.lin['1'] ?? 0) > 0);
|
|
693
|
+
if (absurd >= 0)
|
|
694
|
+
return { leaf: [{ kind: 'input', index: absurd }] };
|
|
695
|
+
const out = grow(whole, wp);
|
|
696
|
+
return 'tree' in out ? out.tree : undefined;
|
|
697
|
+
}
|
|
543
698
|
// Whether each hypothesis reads only names the goal's conclusion (its last hypothesis) reads.
|
|
544
699
|
const nearSeen = new WeakMap();
|
|
545
700
|
const namesOf = (t) => (t === undefined ? [] : [...JSON.stringify(t).matchAll(/"(?:name|map)":"([^"]+)"/g)].map(m => m[1]));
|
|
@@ -1051,9 +1206,9 @@ function triggered(m, laws, inst, build, focus = []) {
|
|
|
1051
1206
|
// fewer hypotheses (a law about pairs of items with 900 instance hypotheses and 30 leaves using a few of them, say).
|
|
1052
1207
|
export const minimal = { on: false };
|
|
1053
1208
|
// The hypotheses a tree's leaves read: each leaf's input rows, and the rows its products multiply.
|
|
1054
|
-
function usedHyps(goal, tree, path = new Map(), out = new Set()) {
|
|
1209
|
+
export function usedHyps(goal, tree, path = new Map(), out = new Set()) {
|
|
1055
1210
|
if ('leaf' in tree) {
|
|
1056
|
-
const rows =
|
|
1211
|
+
const rows = rowsAt(goal, path), n = rows.length, sq = tree.squares?.length ?? 0;
|
|
1057
1212
|
const mark = (i) => {
|
|
1058
1213
|
if (i < n) {
|
|
1059
1214
|
const s = rows[i].src;
|
|
@@ -37,6 +37,7 @@ export declare function readable(id: string, laws: Law[], vars: string[], maps:
|
|
|
37
37
|
binders: string;
|
|
38
38
|
hyp: string;
|
|
39
39
|
call: string;
|
|
40
|
+
have?: (j: number) => string;
|
|
40
41
|
}): string;
|
|
41
42
|
export type ProgressCase = {
|
|
42
43
|
id: string;
|
|
@@ -61,6 +62,10 @@ export declare function mmachineToLean(id: string, m: Machine, laws: Law[], opts
|
|
|
61
62
|
stepLaws?: StepLaw[];
|
|
62
63
|
}): string;
|
|
63
64
|
export declare const hasSets: (m: Machine) => boolean;
|
|
65
|
+
export declare function machineLit(mm: Machine, t: {
|
|
66
|
+
e: (x: E) => string;
|
|
67
|
+
c: (x: C) => string;
|
|
68
|
+
}, ix: (name: string) => number, mx: (name: string) => number): string;
|
|
64
69
|
export declare function smachineToLean(id: string, given: Machine, laws: Law[], opts?: {
|
|
65
70
|
stepLaws?: StepLaw[];
|
|
66
71
|
liveness?: Liveness[];
|
|
@@ -152,6 +157,13 @@ export type LeanItem = {
|
|
|
152
157
|
proof: Cyclic;
|
|
153
158
|
}[];
|
|
154
159
|
after?: (ix: (name: string) => number, mx: (map: string) => number) => string;
|
|
160
|
+
bridge?: CycBridge;
|
|
161
|
+
recur?: CycRecur;
|
|
162
|
+
fold?: {
|
|
163
|
+
fold: Fold;
|
|
164
|
+
laws: Law[];
|
|
165
|
+
};
|
|
166
|
+
data?: true;
|
|
155
167
|
};
|
|
156
168
|
export declare function toLean(items: LeanItem[], opts?: {
|
|
157
169
|
title?: string;
|
|
@@ -160,7 +172,55 @@ export declare function toLean(items: LeanItem[], opts?: {
|
|
|
160
172
|
export declare function leanProject(file: string, opts?: {
|
|
161
173
|
name?: string;
|
|
162
174
|
}): Record<string, string>;
|
|
175
|
+
export type CycBridge = {
|
|
176
|
+
machine: Machine;
|
|
177
|
+
laws: Law[];
|
|
178
|
+
tampered?: {
|
|
179
|
+
what: string;
|
|
180
|
+
machine: Machine;
|
|
181
|
+
}[];
|
|
182
|
+
};
|
|
183
|
+
export type CycRecur = {
|
|
184
|
+
name: string;
|
|
185
|
+
args: string[];
|
|
186
|
+
zero: E;
|
|
187
|
+
next: E;
|
|
188
|
+
lemmas: {
|
|
189
|
+
name: string;
|
|
190
|
+
holds: C;
|
|
191
|
+
}[];
|
|
192
|
+
between: {
|
|
193
|
+
name: string;
|
|
194
|
+
holds: C;
|
|
195
|
+
}[];
|
|
196
|
+
};
|
|
163
197
|
export declare function cyclicToLean(id: string, p: Cyclic, tampered?: {
|
|
164
198
|
what: string;
|
|
165
199
|
proof: Cyclic;
|
|
166
|
-
}[], after?: (ix: (name: string) => number, mx: (map: string) => number) => string
|
|
200
|
+
}[], after?: (ix: (name: string) => number, mx: (map: string) => number) => string, bridge?: CycBridge, recur?: CycRecur, fold?: {
|
|
201
|
+
fold: Fold;
|
|
202
|
+
laws: Law[];
|
|
203
|
+
}): string;
|
|
204
|
+
export declare function cycEncodingLean(id: string, facts: {
|
|
205
|
+
name: string;
|
|
206
|
+
keys: string[];
|
|
207
|
+
holds: C;
|
|
208
|
+
}[], nodes: {
|
|
209
|
+
name: string;
|
|
210
|
+
hyps: C[];
|
|
211
|
+
concl: C;
|
|
212
|
+
}[], b: CycBridge): string;
|
|
213
|
+
export declare function cycRecurCheckLean(id: string, facts: {
|
|
214
|
+
name: string;
|
|
215
|
+
keys: string[];
|
|
216
|
+
holds: C;
|
|
217
|
+
}[], r: CycRecur): string;
|
|
218
|
+
export declare function cycFoldCheckLean(id: string, facts: {
|
|
219
|
+
name: string;
|
|
220
|
+
keys: string[];
|
|
221
|
+
holds: C;
|
|
222
|
+
}[], nodes: {
|
|
223
|
+
name: string;
|
|
224
|
+
hyps: C[];
|
|
225
|
+
concl: C;
|
|
226
|
+
}[], f: Fold, laws: Law[]): string;
|