gdp-wired 0.18.0 → 0.20.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/dist/core/cycles.d.ts +23 -2
- package/dist/core/cycles.js +451 -51
- package/dist/core/cyclic.d.ts +105 -7
- package/dist/core/cyclic.js +800 -103
- package/dist/core/data.d.ts +61 -5
- package/dist/core/data.js +487 -113
- package/dist/core/discover.js +54 -33
- package/dist/core/dsl.d.ts +4 -2
- package/dist/core/dsl.js +47 -12
- package/dist/core/solver.d.ts +3 -0
- package/dist/core/solver.js +5 -0
- package/dist/core/translate.d.ts +30 -0
- package/dist/core/translate.js +578 -0
- package/dist/kernel/prove.d.ts +16 -0
- package/dist/kernel/prove.js +160 -13
- package/dist/lean-kernel/emit.d.ts +36 -1
- package/dist/lean-kernel/emit.js +527 -85
- package/dist/lean-kernel/library.js +2 -1
- package/package.json +1 -1
package/dist/core/discover.js
CHANGED
|
@@ -83,8 +83,8 @@ function linearCands(target, forms, samples, show) {
|
|
|
83
83
|
return dedupe(out.filter(c => samples.every(s => holds(c, s))));
|
|
84
84
|
}
|
|
85
85
|
const dedupe = (cs) => [...new Map(cs.map(c => [c.name, c])).values()];
|
|
86
|
-
// A polynomial in x of degree
|
|
87
|
-
//
|
|
86
|
+
// A polynomial in x of degree 3 at most, exact over the rationals, as d·f = a·x·x·x + b·x·x + c·x + e with whole
|
|
87
|
+
// numbers: fitted to four points (Newton's differences), kept when every sample meets it.
|
|
88
88
|
function closedForm(samples, show) {
|
|
89
89
|
const at = new Map();
|
|
90
90
|
for (const s of samples) {
|
|
@@ -95,22 +95,30 @@ function closedForm(samples, show) {
|
|
|
95
95
|
if (![0, 1, 2].every(x => at.has(x)))
|
|
96
96
|
return [];
|
|
97
97
|
const [f0, f1, f2] = [at.get(0), at.get(1), at.get(2)];
|
|
98
|
+
const keep = (lin) => {
|
|
99
|
+
const g = Object.values(lin).reduce((p, q) => gcd(p, Math.abs(q)), 0);
|
|
100
|
+
const c = { name: '', rel: '=', lin: Object.fromEntries(Object.entries(lin).map(([a, k]) => [a, k / g]).filter(([, k]) => k !== 0)) };
|
|
101
|
+
c.name = nameOf(c, show, 'f');
|
|
102
|
+
return samples.every(s => holds(c, s)) ? [c] : [];
|
|
103
|
+
};
|
|
98
104
|
// f = a x² + b x + c: c = f0, a = (f2 - 2 f1 + f0) / 2, b = f1 - f0 - a; times 2 to keep whole numbers.
|
|
99
105
|
const A = f2 - 2 * f1 + f0, B = 2 * (f1 - f0) - A, C = 2 * f0;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
const square = A === 0 ? [] : keep({ f: 2, xx: -A, x: -B, 1: -C });
|
|
107
|
+
if (square.length || !at.has(3))
|
|
108
|
+
return square;
|
|
109
|
+
// f = Δ0 + Δ1·x + Δ2·x(x − 1)/2 + Δ3·x(x − 1)(x − 2)/6, times 6: x³ by Δ3, x² by 3Δ2 − 3Δ3, x by 6Δ1 − 3Δ2 + 2Δ3.
|
|
110
|
+
const d1 = f1 - f0, d2 = f2 - 2 * f1 + f0, d3 = at.get(3) - 3 * f2 + 3 * f1 - f0;
|
|
111
|
+
if (d3 === 0)
|
|
112
|
+
return []; // degree 2 at most: above, or linear (the bounds find it)
|
|
113
|
+
return keep({ f: 6, xxx: -d3, xx: -(3 * d2 - 3 * d3), x: -(6 * d1 - 3 * d2 + 2 * d3), 1: -6 * f0 });
|
|
106
114
|
}
|
|
107
115
|
// The law a goal is of (the kernel names each goal after its law: '<law> @step').
|
|
108
116
|
const lawOfGoal = (goal) => goal.replace(/ @(start|step|step law|\d+ steps|leaf|node)$/, '');
|
|
109
117
|
const isGoalOf = (goal) => (c) => { const law = lawOfGoal(goal); return law === c.name || law === `${c.name} at y`; };
|
|
110
118
|
const gcd = (a, b) => (b === 0 ? Math.abs(a) : gcd(b, a % b));
|
|
111
119
|
// --- recurrences ---------------------------------------------------------------------------------------------------
|
|
112
|
-
const showRecur = (a) => (a === 'f' ? 'f(x)' : a === 'g' ? 'f(y)' : a === 'xx' ? 'x·x' : a);
|
|
113
|
-
const srcRecur = (a) => (a === 'xx' ? 'x.mul(x)' : a);
|
|
120
|
+
const showRecur = (a) => (a === 'f' ? 'f(x)' : a === 'g' ? 'f(y)' : a === 'xx' ? 'x·x' : a === 'xxx' ? 'x·x·x' : a);
|
|
121
|
+
const srcRecur = (a) => (a === 'xx' ? 'x.mul(x)' : a === 'xxx' ? 'x.mul(x).mul(x)' : a);
|
|
114
122
|
// The function's values: x from 0 to 40 at a few values of each further argument (all of them for two at most).
|
|
115
123
|
function recurSamples(d, args) {
|
|
116
124
|
const f = (typeof d.js === 'function' ? d.js : compileFunction([], `return (${d.js})`)());
|
|
@@ -128,7 +136,7 @@ function recurSamples(d, args) {
|
|
|
128
136
|
break;
|
|
129
137
|
throw e;
|
|
130
138
|
}
|
|
131
|
-
out.push({ x, f: y, xx: x * x, ...Object.fromEntries(args.map((a, i) => [a, t[i]])) });
|
|
139
|
+
out.push({ x, f: y, xx: x * x, xxx: x * x * x, ...Object.fromEntries(args.map((a, i) => [a, t[i]])) });
|
|
132
140
|
}
|
|
133
141
|
return out;
|
|
134
142
|
}
|
|
@@ -167,9 +175,9 @@ function recurCands(samples, args) {
|
|
|
167
175
|
c.name = nameOf(c, showRecur, 'g');
|
|
168
176
|
return { lemmas, between };
|
|
169
177
|
}
|
|
170
|
-
const recurAtoms = (args, x, f, env, y, g) => ({ x, f, xx: x.mul(x), ...(y && g ? { y, g } : {}), ...Object.fromEntries(args.map(a => [a, env[a]])) });
|
|
178
|
+
const recurAtoms = (args, x, f, env, y, g) => ({ x, f, xx: x.mul(x), xxx: x.mul(x).mul(x), ...(y && g ? { y, g } : {}), ...Object.fromEntries(args.map(a => [a, env[a]])) });
|
|
171
179
|
const recurSrc = (c, args, between) => {
|
|
172
|
-
const reads = (a) => Object.keys(c.lin).includes(a) || (a === 'x' && 'xx' in c.lin);
|
|
180
|
+
const reads = (a) => Object.keys(c.lin).includes(a) || (a === 'x' && ('xx' in c.lin || 'xxx' in c.lin));
|
|
173
181
|
const names = between ? ['x', 'f', 'y', 'g'] : ['x', 'f'];
|
|
174
182
|
let unused = 0;
|
|
175
183
|
const params = [...names.map(n => (reads(n) ? n : '_'.repeat(++unused))), ...(args.some(reads) ? [`{${args.filter(reads).join(', ')}}`] : [])];
|
|
@@ -268,27 +276,40 @@ function discoverSearch(r, laws, stepLaws, first) {
|
|
|
268
276
|
if (first.proven && first.checked)
|
|
269
277
|
return { verdict: first, rule: r, found: {} };
|
|
270
278
|
const { params, fns, lists } = r.parts, ps = natParams(params);
|
|
271
|
-
// Per function and per list: what is proven, made into the rule.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
if (out && out.kept.length) {
|
|
287
|
-
rule = out.rule;
|
|
288
|
-
listKept[part[0]] = out.kept;
|
|
279
|
+
// Per function and per list: what is proven, made into the rule. A function with a closed form (of degree 2 or 3,
|
|
280
|
+
// fitted exactly) first with it alone: it decides the function, and the bounds beside it, which the closed form
|
|
281
|
+
// implies only through products, are slow to prove by induction; the rest where the laws aren't proven with it.
|
|
282
|
+
const cands = new Map(fns.flatMap(([n, d]) => { const src = recurOf(d); if (!src)
|
|
283
|
+
return []; const args = [...(src.def.args ?? [])]; return [[n, recurCands(recurSamples(d, args), args)]]; }));
|
|
284
|
+
const exact = (c) => c.rel === '=' && ('xx' in c.lin || 'xxx' in c.lin);
|
|
285
|
+
const makeFns = (closedOnly) => {
|
|
286
|
+
const out = {};
|
|
287
|
+
for (const [n, d] of fns) {
|
|
288
|
+
const cs = cands.get(n);
|
|
289
|
+
if (!cs)
|
|
290
|
+
continue;
|
|
291
|
+
const closed = cs.lemmas.filter(exact), made = closedOnly && closed.length ? houdiniRecur(d, closed, []) : houdiniRecur(d, cs.lemmas, cs.between);
|
|
292
|
+
if (made && (made.lemmas.length || made.between.length))
|
|
293
|
+
out[n] = made;
|
|
289
294
|
}
|
|
290
|
-
|
|
291
|
-
|
|
295
|
+
return out;
|
|
296
|
+
};
|
|
297
|
+
const withLists = (fnMade) => {
|
|
298
|
+
let rule = r.withParts(Object.fromEntries(Object.entries(fnMade).map(([n, m]) => [n, m.d])));
|
|
299
|
+
const listKept = {};
|
|
300
|
+
for (const part of lists) {
|
|
301
|
+
const out = houdiniList(rule, part, listCands(part, params, listSamples(part, params)), ps);
|
|
302
|
+
if (out && out.kept.length) {
|
|
303
|
+
rule = out.rule;
|
|
304
|
+
listKept[part[0]] = out.kept;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return { fnMade, rule, listKept, verdict: proveSteps(rule, laws, stepLaws, { search: false }) };
|
|
308
|
+
};
|
|
309
|
+
let made = [...cands.values()].some(cs => cs.lemmas.some(exact)) ? withLists(makeFns(true)) : undefined;
|
|
310
|
+
if (!made || !(made.verdict.proven && made.verdict.checked))
|
|
311
|
+
made = withLists(makeFns(false));
|
|
312
|
+
const { fnMade, rule, listKept, verdict } = made;
|
|
292
313
|
const found = () => ({
|
|
293
314
|
...Object.fromEntries(Object.entries(fnMade).map(([n, m]) => { const args = [...(recurOf(m.d).def.args ?? [])]; return [n, { ...(m.lemmas.length ? { lemmas: m.lemmas.map(c => recurSrc(c, args, false)) } : {}), ...(m.between.length ? { between: m.between.map(c => recurSrc(c, args, true)) } : {}) }]; })),
|
|
294
315
|
...Object.fromEntries(Object.entries(listKept).map(([n, cs]) => [n, { lemmas: cs.map(c => listSrc(c, Object.keys(lists.find(l => l[0] === n)[2]), ps, c.target)) }])),
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -594,7 +594,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
594
594
|
leadsTo(name: string, spec: {
|
|
595
595
|
from: (env: P & S & M & Su) => Cond;
|
|
596
596
|
to: (env: P & S & M & Su) => Cond;
|
|
597
|
-
rank
|
|
597
|
+
rank?: (env: P & S & M & Su) => TermLike | TermLike[];
|
|
598
598
|
helpful: ((env: P & S & M & Su) => Record<string, TermLike>) | {
|
|
599
599
|
when?: (env: P & S & M & Su) => Cond;
|
|
600
600
|
request: (env: P & S & M & Su) => Record<string, TermLike>;
|
|
@@ -828,7 +828,9 @@ export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj
|
|
|
828
828
|
export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[], opts?: ProveOptions): Verdict;
|
|
829
829
|
export type { StepLaw };
|
|
830
830
|
export type { Liveness };
|
|
831
|
-
export declare function proveLeadsTo(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], l: Liveness
|
|
831
|
+
export declare function proveLeadsTo(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], l: Liveness, opts?: {
|
|
832
|
+
ms?: number;
|
|
833
|
+
}): Verdict;
|
|
832
834
|
export declare const proveDecision: (d: Decision) => Verdict;
|
|
833
835
|
export declare function crossCheckDecision(d: Decision, opts?: {
|
|
834
836
|
trials?: number;
|
package/dist/core/dsl.js
CHANGED
|
@@ -18,11 +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, foldNodes, proveCyclic, quietly, recurNodes, translatedProof, uncovered } 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
|
+
import { cycleLemmas, findLeadsTo, findRank } from "./cycles.js";
|
|
26
26
|
// --- terms and conditions -------------------------------------------------------------------------------------------
|
|
27
27
|
const naturalConst = (v) => {
|
|
28
28
|
if (!Number.isSafeInteger(v) || v < 0)
|
|
@@ -1410,6 +1410,10 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1410
1410
|
// The lemmas: x counting up from 0 and fx its value, each lemma a law of that machine.
|
|
1411
1411
|
let verdict = { proven: true, checked: true };
|
|
1412
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;
|
|
1413
1417
|
if (stated.length) {
|
|
1414
1418
|
// A start that reads a function is a param given as that start by a fact (a start reads the params alone).
|
|
1415
1419
|
const zeroReads = JSON.stringify(zero).includes('"op":"get"');
|
|
@@ -1421,9 +1425,17 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1421
1425
|
const laws = Object.entries(lemmas).map(([n, f]) => r.law(n, (env) => f(env.x, env.fx, envAt(args.map(a => env[a])))));
|
|
1422
1426
|
// (Its machine's proof timed alone: the recurrence is compared below as its own instance.)
|
|
1423
1427
|
const t0 = performance.now();
|
|
1428
|
+
usualProof.last = undefined;
|
|
1424
1429
|
verdict = quietly(() => proveRule(r, laws, { search: false }));
|
|
1430
|
+
lemmaProof = usualProof.last;
|
|
1425
1431
|
if (cyclicOn())
|
|
1426
|
-
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
|
+
// (the usual trees checked already, inside quietly: where no cyclic proof is made they are the certificate)
|
|
1434
|
+
if (certOn() && verdict.proven && verdict.checked) {
|
|
1435
|
+
const c = certCyclic('recurrence', name, translatedRecur({ lemmas: lemmaProof }), () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0);
|
|
1436
|
+
if (!c.checked)
|
|
1437
|
+
uncovered('recurrence', name, c.why);
|
|
1438
|
+
}
|
|
1427
1439
|
failed('lemmas', verdict);
|
|
1428
1440
|
proofs.lemmas = { machine: r.machine, laws };
|
|
1429
1441
|
}
|
|
@@ -1460,9 +1472,15 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1460
1472
|
.step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(at(env))) }));
|
|
1461
1473
|
const laws = [...Object.entries(lemmas).map(([n, l]) => r.law(`${n} at y`, (env) => l(env.y, env.fy, envAt(at(env))))),
|
|
1462
1474
|
...Object.entries(between).map(([n, l]) => r.law(n, (env) => l(env.x, env.fx, env.y, env.fy, envAt(at(env)))))];
|
|
1463
|
-
|
|
1475
|
+
usualProof.last = undefined;
|
|
1476
|
+
const t0 = performance.now(), v = quietly(() => proveRule(r, laws, { search: false })), usualMs = performance.now() - t0, betweenProof = usualProof.last;
|
|
1464
1477
|
if (cyclicOn())
|
|
1465
|
-
crossCyclic('recurrence, between', name, v.proven && v.checked, () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs);
|
|
1478
|
+
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 }));
|
|
1479
|
+
if (certOn() && v.proven && v.checked) {
|
|
1480
|
+
const c = certCyclic('recurrence, between', name, betweenProof && translatedRecur({ lemmas: lemmaProof, between: betweenProof }), () => { const { facts, specs } = recurNodes(recurCase(pairs)); return proveCyclic(facts, specs); }, usualMs);
|
|
1481
|
+
if (!c.checked)
|
|
1482
|
+
uncovered('recurrence, between', name, c.why);
|
|
1483
|
+
}
|
|
1466
1484
|
failed('laws between two points', v);
|
|
1467
1485
|
proofs.between = { machine: r.machine, laws };
|
|
1468
1486
|
verdict = { proven: true, checked: verdict.checked && v.checked };
|
|
@@ -1828,10 +1846,11 @@ export class Rule {
|
|
|
1828
1846
|
// rank: ({t, served}) => t.plus(1).minus(served), helpful: () => ({op: 1})})
|
|
1829
1847
|
// batches.leadsTo('every batch is done', {from: () => yes, to: ({waiting, items}) => waiting.eq(0).and(items.eq(0)),
|
|
1830
1848
|
// rank: ({waiting, items}) => [waiting, items], helpful: [{when: ({items}) => items.gt(0), request: () => ({op: 1})}, {when: ({items}) => items.eq(0), request: () => ({op: 0})}]})
|
|
1849
|
+
// With no rank, proveLeadsTo finds one from the rule's runs (core/cycles.ts's findLeadsTo).
|
|
1831
1850
|
leadsTo(name, spec) {
|
|
1832
1851
|
const env = this.env({ flags: false, inputs: false, queries: false });
|
|
1833
1852
|
const req = (f) => Object.fromEntries(Object.entries(f(env)).map(([n, t]) => [n, termOf(t)]));
|
|
1834
|
-
const r = spec.rank(env), rank = Array.isArray(r) ? r.map(termOf) : termOf(r);
|
|
1853
|
+
const r = spec.rank ? spec.rank(env) : [], rank = Array.isArray(r) ? r.map(termOf) : termOf(r);
|
|
1835
1854
|
const base = { name, from: spec.from(env).c, to: spec.to(env).c, rank };
|
|
1836
1855
|
return typeof spec.helpful === 'function' ? { ...base, request: req(spec.helpful) } : { ...base, helpful: spec.helpful.map(h => ({ ...(h.when ? { when: h.when(env).c } : {}), request: req(h.request) })) };
|
|
1837
1856
|
}
|
|
@@ -2504,7 +2523,7 @@ export class Unproven extends Error {
|
|
|
2504
2523
|
this.verdict = verdict;
|
|
2505
2524
|
}
|
|
2506
2525
|
}
|
|
2507
|
-
const goalsVerdict = (goals) => {
|
|
2526
|
+
const goalsVerdict = (goals, checkIt = true) => {
|
|
2508
2527
|
const trees = {};
|
|
2509
2528
|
for (const g of goals) {
|
|
2510
2529
|
const out = grow(g);
|
|
@@ -2512,7 +2531,7 @@ const goalsVerdict = (goals) => {
|
|
|
2512
2531
|
return { proven: false, checked: false, goal: g.name, stuck: out.stuck, ...(out.witness ? { witness: out.witness } : {}), ...(out.why ? { why: out.why } : {}) };
|
|
2513
2532
|
trees[g.name] = out.tree;
|
|
2514
2533
|
}
|
|
2515
|
-
return { proven: true, checked: goals.every(g => check(g, trees[g.name])), trees };
|
|
2534
|
+
return { proven: true, checked: !checkIt || goals.every(g => check(g, trees[g.name])), trees };
|
|
2516
2535
|
};
|
|
2517
2536
|
// The kinds' laws first, those the caller didn't pass already (by name).
|
|
2518
2537
|
const withKinds = (r, laws) => [...r.kindLaws().filter(k => !laws.some(l => l.name === k.name)), ...laws];
|
|
@@ -2643,7 +2662,12 @@ function searched(r, laws, stepLaws, v, opts) {
|
|
|
2643
2662
|
void _stats;
|
|
2644
2663
|
return { ...verdict, found: { laws: found, ...(lemmas ? { lemmas } : {}) } };
|
|
2645
2664
|
}
|
|
2646
|
-
export function proveLeadsTo(r, laws, l) {
|
|
2665
|
+
export function proveLeadsTo(r, laws, l, opts = {}) {
|
|
2666
|
+
if (Array.isArray(l.rank) && !l.rank.length) {
|
|
2667
|
+
const f = findLeadsTo(r.machine, withKinds(r, laws), l, { params: (rand, bound) => r.randomParams(rand, bound), input: (rand, bound, ps) => r.randomInput(rand, bound, ps) }, opts);
|
|
2668
|
+
return { proven: f.proven, checked: f.checked, ...(f.goal ? { goal: f.goal, stuck: f.stuck } : {}), ...(f.witness ? { witness: f.witness } : {}), ...(f.why ? { why: f.why } : {}),
|
|
2669
|
+
...(f.found ? { found: { rank: f.found.rank, ...(f.laws?.length ? { laws: f.laws.map((law, i) => ({ law, text: f.found.laws[i] ?? law.name })) } : {}) } } : {}) };
|
|
2670
|
+
}
|
|
2647
2671
|
const out = proveLiveness(r.machine, withKinds(r, laws), l);
|
|
2648
2672
|
return { proven: out.proven, checked: out.checked, ...(out.failed ? { goal: out.failed.goal, stuck: out.failed.stuck, ...(out.failed.witness ? { witness: out.failed.witness } : {}), ...(out.failed.why ? { why: out.failed.why } : {}) } : {}) };
|
|
2649
2673
|
}
|
|
@@ -2679,6 +2703,8 @@ export function crossCheckDecision(d, opts = {}) {
|
|
|
2679
2703
|
// With no rank stated, the rank is searched for from the loop's runs (core/cycles.ts's findRank), with the invariants it
|
|
2680
2704
|
// needs (inferLaws), within `ms` (10 s); the verdict is the kernel's on the rank and the laws found, in `found`.
|
|
2681
2705
|
export const proveLoop = (l, laws, opts = {}) => {
|
|
2706
|
+
if (cyclicOn() || certOn())
|
|
2707
|
+
uncovered('loop', l.loop.name, 'a claim about runs that never end');
|
|
2682
2708
|
if (l.hasRank)
|
|
2683
2709
|
return goalsVerdict(loopObligations(l.loop, laws, l.rankTerms));
|
|
2684
2710
|
const out = findRank(l.loop, laws, opts);
|
|
@@ -2800,9 +2826,18 @@ export class FoldRule {
|
|
|
2800
2826
|
export const fold = (name) => new FoldRule({ name, params: [], inputs: [], pre: [], assume: [] });
|
|
2801
2827
|
// A fold's laws proven over every tree (a rose fold's own laws, its children's lemmas, with them).
|
|
2802
2828
|
export const proveFold = (f, laws) => {
|
|
2803
|
-
|
|
2829
|
+
// (by default the certificate a cyclic proof, core/cyclic.ts's certCyclic, the usual trees its input; where none is
|
|
2830
|
+
// made, the usual trees checked)
|
|
2831
|
+
const all = [...(f.lemmas ?? []), ...laws], t0 = performance.now(), v = goalsVerdict(foldObligations(f.fold, all), !certOn()), usualMs = performance.now() - t0;
|
|
2804
2832
|
if (cyclicOn())
|
|
2805
|
-
crossCyclic('fold', f.fold.name, v.proven && v.checked, () => { const { facts, specs } = foldNodes(f.fold, all); return proveCyclic(facts, specs); }, usualMs);
|
|
2833
|
+
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)));
|
|
2834
|
+
if (certOn() && v.proven) {
|
|
2835
|
+
const c = 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);
|
|
2836
|
+
if (!c.checked) {
|
|
2837
|
+
uncovered('fold', f.fold.name, c.why);
|
|
2838
|
+
v.checked = foldObligations(f.fold, all).every(g => v.trees?.[g.name] !== undefined && check(g, v.trees[g.name]));
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2806
2841
|
return v;
|
|
2807
2842
|
};
|
|
2808
2843
|
const asTerm = (t) => (t instanceof Term ? t : lit(t));
|
package/dist/core/solver.d.ts
CHANGED
|
@@ -50,5 +50,8 @@ export type Verdict = {
|
|
|
50
50
|
kind: 'unknown';
|
|
51
51
|
point?: Record<string, number>;
|
|
52
52
|
};
|
|
53
|
+
export declare const work: {
|
|
54
|
+
n: number;
|
|
55
|
+
};
|
|
53
56
|
export declare function solve(constraints: Constraint[], maxRows?: number): Verdict;
|
|
54
57
|
export declare function refuteFast(constraints: Constraint[], maxRows?: number): Verdict | undefined;
|
package/dist/core/solver.js
CHANGED
|
@@ -56,7 +56,10 @@ const gcd = (a, b) => (b === 0 ? Math.abs(a) : gcd(b, a % b));
|
|
|
56
56
|
const vars = (lin) => Object.keys(lin).filter(key => key !== '1');
|
|
57
57
|
// maxRows: elimination can square the rows each round; past this many live rows, or a round that would make more than
|
|
58
58
|
// five times as many before merging them, it gives up with `unknown`.
|
|
59
|
+
// The rows the solver has read and made, which a counted search reads as its clock (kernel/prove.ts's `counted`).
|
|
60
|
+
export const work = { n: 0 };
|
|
59
61
|
export function solve(constraints, maxRows = 20_000) {
|
|
62
|
+
work.n += constraints.length;
|
|
60
63
|
const steps = [];
|
|
61
64
|
const lins = [];
|
|
62
65
|
const push = (step, lin) => (steps.push(step), lins.push(lin), lins.length - 1);
|
|
@@ -173,6 +176,7 @@ export function solve(constraints, maxRows = 20_000) {
|
|
|
173
176
|
break;
|
|
174
177
|
if (choice.pos.length * choice.neg.length > 5 * maxRows)
|
|
175
178
|
return { kind: 'unknown' };
|
|
179
|
+
work.n += choice.pos.length * choice.neg.length;
|
|
176
180
|
exact &&= choice.isExact;
|
|
177
181
|
eliminated.push({ x: choice.x, rows: [...choice.pos, ...choice.neg] });
|
|
178
182
|
const next = rows.filter(r => (lins[r][choice.x] ?? 0) === 0);
|
|
@@ -295,6 +299,7 @@ export function refuteFast(constraints, maxRows = 2_000) {
|
|
|
295
299
|
z[j] -= T[r][j];
|
|
296
300
|
const eps = 1e-9;
|
|
297
301
|
for (let iter = 0; iter < 50 * (R + m); iter++) {
|
|
302
|
+
work.n += 1 + ((R * cols) >> 8);
|
|
298
303
|
// Bland's rule: the first column with a negative reduced cost enters (no cycling on degenerate systems).
|
|
299
304
|
let enter = -1;
|
|
300
305
|
for (let j = 0; j < cols; j++)
|
|
@@ -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;
|