gdp-wired 0.19.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 +18 -4
- package/dist/core/cyclic.js +319 -263
- 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 +31 -12
- package/dist/core/solver.d.ts +3 -0
- package/dist/core/solver.js +5 -0
- package/dist/core/translate.js +10 -4
- package/dist/kernel/prove.d.ts +3 -0
- package/dist/kernel/prove.js +95 -50
- package/dist/lean-kernel/emit.d.ts +2 -1
- package/dist/lean-kernel/emit.js +140 -54
- 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
|
@@ -19,10 +19,10 @@ import { carriedView, compileFunction, evalC, evalE, interpret, loadJs, numbersO
|
|
|
19
19
|
import { allowed, keysOf, requestEnv, watch } from "./watch.js";
|
|
20
20
|
import { check, foldObligations, loopObligations } from "../kernel/kernel.js";
|
|
21
21
|
import { factsFor, grow, proveLaws, proveLiveness, proveStepLaws, usualProof, withDeadline } from "../kernel/prove.js";
|
|
22
|
-
import { certCyclic, certOn, crossCyclic, cyclicOn,
|
|
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)
|
|
@@ -1430,8 +1430,12 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1430
1430
|
lemmaProof = usualProof.last;
|
|
1431
1431
|
if (cyclicOn())
|
|
1432
1432
|
crossCyclic('recurrence', name, verdict.proven && verdict.checked, () => { const { facts, specs } = recurNodes(recurCase([])); return proveCyclic(facts, specs); }, performance.now() - t0, translatedRecur({ lemmas: lemmaProof }));
|
|
1433
|
-
|
|
1434
|
-
|
|
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
|
+
}
|
|
1435
1439
|
failed('lemmas', verdict);
|
|
1436
1440
|
proofs.lemmas = { machine: r.machine, laws };
|
|
1437
1441
|
}
|
|
@@ -1472,8 +1476,11 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
|
|
|
1472
1476
|
const t0 = performance.now(), v = quietly(() => proveRule(r, laws, { search: false })), usualMs = performance.now() - t0, betweenProof = usualProof.last;
|
|
1473
1477
|
if (cyclicOn())
|
|
1474
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 }));
|
|
1475
|
-
if (certOn() && v.proven && v.checked)
|
|
1476
|
-
|
|
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
|
+
}
|
|
1477
1484
|
failed('laws between two points', v);
|
|
1478
1485
|
proofs.between = { machine: r.machine, laws };
|
|
1479
1486
|
verdict = { proven: true, checked: verdict.checked && v.checked };
|
|
@@ -1839,10 +1846,11 @@ export class Rule {
|
|
|
1839
1846
|
// rank: ({t, served}) => t.plus(1).minus(served), helpful: () => ({op: 1})})
|
|
1840
1847
|
// batches.leadsTo('every batch is done', {from: () => yes, to: ({waiting, items}) => waiting.eq(0).and(items.eq(0)),
|
|
1841
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).
|
|
1842
1850
|
leadsTo(name, spec) {
|
|
1843
1851
|
const env = this.env({ flags: false, inputs: false, queries: false });
|
|
1844
1852
|
const req = (f) => Object.fromEntries(Object.entries(f(env)).map(([n, t]) => [n, termOf(t)]));
|
|
1845
|
-
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);
|
|
1846
1854
|
const base = { name, from: spec.from(env).c, to: spec.to(env).c, rank };
|
|
1847
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) })) };
|
|
1848
1856
|
}
|
|
@@ -2654,7 +2662,12 @@ function searched(r, laws, stepLaws, v, opts) {
|
|
|
2654
2662
|
void _stats;
|
|
2655
2663
|
return { ...verdict, found: { laws: found, ...(lemmas ? { lemmas } : {}) } };
|
|
2656
2664
|
}
|
|
2657
|
-
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
|
+
}
|
|
2658
2671
|
const out = proveLiveness(r.machine, withKinds(r, laws), l);
|
|
2659
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 } : {}) } : {}) };
|
|
2660
2673
|
}
|
|
@@ -2691,7 +2704,7 @@ export function crossCheckDecision(d, opts = {}) {
|
|
|
2691
2704
|
// needs (inferLaws), within `ms` (10 s); the verdict is the kernel's on the rank and the laws found, in `found`.
|
|
2692
2705
|
export const proveLoop = (l, laws, opts = {}) => {
|
|
2693
2706
|
if (cyclicOn() || certOn())
|
|
2694
|
-
|
|
2707
|
+
uncovered('loop', l.loop.name, 'a claim about runs that never end');
|
|
2695
2708
|
if (l.hasRank)
|
|
2696
2709
|
return goalsVerdict(loopObligations(l.loop, laws, l.rankTerms));
|
|
2697
2710
|
const out = findRank(l.loop, laws, opts);
|
|
@@ -2813,12 +2826,18 @@ export class FoldRule {
|
|
|
2813
2826
|
export const fold = (name) => new FoldRule({ name, params: [], inputs: [], pre: [], assume: [] });
|
|
2814
2827
|
// A fold's laws proven over every tree (a rose fold's own laws, its children's lemmas, with them).
|
|
2815
2828
|
export const proveFold = (f, laws) => {
|
|
2816
|
-
// (
|
|
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)
|
|
2817
2831
|
const all = [...(f.lemmas ?? []), ...laws], t0 = performance.now(), v = goalsVerdict(foldObligations(f.fold, all), !certOn()), usualMs = performance.now() - t0;
|
|
2818
2832
|
if (cyclicOn())
|
|
2819
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)));
|
|
2820
|
-
if (certOn() && v.proven)
|
|
2821
|
-
|
|
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
|
+
}
|
|
2822
2841
|
return v;
|
|
2823
2842
|
};
|
|
2824
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++)
|
package/dist/core/translate.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { add, refuteFast, scale, solve } from "./solver.js";
|
|
2
2
|
import { checkRefutation } from "./check.js";
|
|
3
3
|
import { canon, canonFlip, columnTerm, factorsOf, isCell, isMonomial, leafConstraintsReusing, linear, substE, truth, withProducts } from "../kernel/kernel.js";
|
|
4
|
-
import { cellProducts, grow } from "../kernel/prove.js";
|
|
4
|
+
import { cellProducts, clock, grow } from "../kernel/prove.js";
|
|
5
5
|
const omega = 'O$';
|
|
6
|
+
const debugOn = () => !!globalThis.process?.env?.GDP_CYCLIC_DEBUG;
|
|
6
7
|
// The kernel's product of two linear forms (kernel.ts's `times`): each monomial its factors sorted, joined by '*'.
|
|
7
8
|
function times(a, b) {
|
|
8
9
|
const out = [];
|
|
@@ -209,6 +210,10 @@ export function translate(part, whole, tree, ren0, st, until, pp = new Map(), wp
|
|
|
209
210
|
}
|
|
210
211
|
if (ok && checkRefutation(withProducts(rw, products, squares), leaf))
|
|
211
212
|
return { leaf, ...(tree.products ? { products } : {}), ...(tree.squares ? { squares: squares } : {}) };
|
|
213
|
+
if (debugOn()) {
|
|
214
|
+
const missing = tree.leaf.flatMap(s => (s.kind === 'input' && s.index < rp.length && map(s.index) === undefined ? [s.index] : []));
|
|
215
|
+
console.error(`[leaf] ${whole.name}: ${ok ? 'rows found, refutation refused' : `${missing.length} rows missing`}${missing.slice(0, 3).map(i => `; ${JSON.stringify(rp[i].lin)} as ${JSON.stringify(ren(rp[i].lin, wp) ?? 'no form')}${rp[i].src ? ` (${JSON.stringify(rp[i].src)})` : ''}`).join('')}`);
|
|
216
|
+
}
|
|
212
217
|
return close(whole, wp, st, until);
|
|
213
218
|
}
|
|
214
219
|
export function readsFirst(whole, reads, ren, inner, st, until, wp = new Map(), swaps = []) {
|
|
@@ -289,7 +294,8 @@ export function decide(whole, conds, inner, st, until, wp = new Map()) {
|
|
|
289
294
|
const lin = atoms[0], id = canon(lin);
|
|
290
295
|
st.splits++;
|
|
291
296
|
const yes = decide(whole, conds.slice(i), inner, st, until, new Map(wp).set(id, { lin, holds: true }));
|
|
292
|
-
|
|
297
|
+
// (the other side refuted by the node's rows as they are, a time below the steps before it say, before it is grown)
|
|
298
|
+
const np = new Map(wp).set(id, { lin, holds: false }), no = yes && (leafAt(whole, np, true, st) ?? regrow(whole, np, until));
|
|
293
299
|
return yes && no ? { split: lin, yes, no } : undefined;
|
|
294
300
|
}
|
|
295
301
|
return inner(wp);
|
|
@@ -439,7 +445,7 @@ export function close(g, wp, st, until) {
|
|
|
439
445
|
if (l)
|
|
440
446
|
return l;
|
|
441
447
|
const pick = choiceAtom(g, wp) ?? cellAtom(g, wp), om = pick ? undefined : omegaPair(g, wp), next = pick ?? om?.l;
|
|
442
|
-
if (next &&
|
|
448
|
+
if (next && clock() < until) {
|
|
443
449
|
st.splits++;
|
|
444
450
|
const id = canon(next), y = close(g, new Map(wp).set(id, { lin: next, holds: true }), st, until);
|
|
445
451
|
const no = new Map(wp).set(id, { lin: next, holds: false }), n = y && (om?.q ? apart(g, no, next, om.q, st, until) : close(g, no, st, until));
|
|
@@ -509,7 +515,7 @@ export function carry(g, nFacts, nUses, st, until) {
|
|
|
509
515
|
};
|
|
510
516
|
const leafOf = (wp, solveIt) => leafAt(g, wp, solveIt, st), final = (wp) => close(g, wp, st, until);
|
|
511
517
|
const node = (wp, guardsDone) => {
|
|
512
|
-
if (
|
|
518
|
+
if (clock() > until)
|
|
513
519
|
return undefined;
|
|
514
520
|
// A hypothesis false at the path: the law at t holds there, or a law it is carried from (its guard true) fails.
|
|
515
521
|
if (truth(law, wp) === true || parts.some(p => (p.guard === undefined || truth(p.guard, wp) === true) && truth(p.concl, wp) === false))
|
package/dist/kernel/prove.d.ts
CHANGED
|
@@ -38,6 +38,9 @@ export type Outcome = {
|
|
|
38
38
|
path: Path;
|
|
39
39
|
why?: string;
|
|
40
40
|
};
|
|
41
|
+
export declare const clock: () => number;
|
|
42
|
+
export declare function counted<T>(perMs: number, f: () => T): T;
|
|
43
|
+
export declare const withinDeadline: (until: number) => number;
|
|
41
44
|
export declare function withDeadline<T>(until: number, f: () => T): T;
|
|
42
45
|
export declare function grow(goal: Goal, path?: Path, budget?: {
|
|
43
46
|
nodes: number;
|
package/dist/kernel/prove.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { add, k, refuteFast, scale, solve, sub, v } from "../core/solver.js";
|
|
1
|
+
import { add, k, refuteFast, scale, solve, sub, v, work } from "../core/solver.js";
|
|
2
2
|
import { checkRefutation } from "../core/check.js";
|
|
3
|
-
import { allowNode,
|
|
3
|
+
import { allowNode, allowTranslated, certCyclic, certOn, certSearch, closingInsts, crossCyclic, cyclicOn, cycLog, hasQuantified, machineNodes, proveCyclic, reqTS, searchedStepNodes, skMachine, timedKind, withWitness, translatedProof, uncovered, uncoveredWhy } from "../core/cyclic.js";
|
|
4
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";
|
|
5
5
|
// The atoms the path leaves open, in evaluation order: deciding any of them makes progress.
|
|
6
6
|
function openE(e, path, out) {
|
|
@@ -193,6 +193,28 @@ export const cellProducts = { on: false };
|
|
|
193
193
|
// A deadline every goal grown inside `withDeadline` keeps, beside its own budget: a search that proves many things (core/
|
|
194
194
|
// infer.ts, core/discover.ts, the search proveRule falls back on) bounds them all; a goal past it is left `unknown`.
|
|
195
195
|
let deadline = Infinity;
|
|
196
|
+
// The clock every budget here reads (grow's `until`, withDeadline's; core/cyclic.ts's and core/data.ts's searches): wall
|
|
197
|
+
// time, or within `counted` the work the prover has done (the solver's rows read and made, each node's rows), `perMs`
|
|
198
|
+
// units a millisecond, so that what a search's budget lets it try, and so its verdict, doesn't depend on the machine's
|
|
199
|
+
// speed. A counted search inside another reads the outer's clock.
|
|
200
|
+
let rate = 0;
|
|
201
|
+
export const clock = () => (rate ? work.n / rate : performance.now());
|
|
202
|
+
export function counted(perMs, f) {
|
|
203
|
+
if (rate)
|
|
204
|
+
return f();
|
|
205
|
+
const outer = deadline;
|
|
206
|
+
rate = perMs;
|
|
207
|
+
deadline = Infinity;
|
|
208
|
+
try {
|
|
209
|
+
return f();
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
rate = 0;
|
|
213
|
+
deadline = outer;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// A budget's end, within the deadline (a translation's own bound, core/cyclic.ts's, kept by the export's GDP_CERT_MS).
|
|
217
|
+
export const withinDeadline = (until) => Math.min(until, deadline);
|
|
196
218
|
export function withDeadline(until, f) {
|
|
197
219
|
const outer = deadline;
|
|
198
220
|
deadline = Math.min(outer, until);
|
|
@@ -205,7 +227,7 @@ export function withDeadline(until, f) {
|
|
|
205
227
|
}
|
|
206
228
|
// `units: false`: the goal's own order, no units first (a caller after a model, not a proof: a run guided by the
|
|
207
229
|
// prover's witnesses, say, whose models units would steer).
|
|
208
|
-
export function grow(goal, path = new Map(), budget = { nodes: 0, until:
|
|
230
|
+
export function grow(goal, path = new Map(), budget = { nodes: 0, until: clock() + Math.max(20_000, 1_000 * goal.hyps.length) }) {
|
|
209
231
|
if (deadline < (budget.until ?? Infinity))
|
|
210
232
|
budget.until = deadline;
|
|
211
233
|
if (budget.units === false && unitsNow) {
|
|
@@ -263,11 +285,12 @@ function growPlain(goal, path, budget) {
|
|
|
263
285
|
return { stuck: 'unknown', path, why: 'cap' };
|
|
264
286
|
if (profile.on)
|
|
265
287
|
profile.nodes++;
|
|
266
|
-
if (++budget.nodes > 200_000 || (budget.until !== undefined &&
|
|
288
|
+
if (++budget.nodes > 200_000 || (budget.until !== undefined && clock() > budget.until))
|
|
267
289
|
return { stuck: 'unknown', path, why: outOfBudget(goal, budget.nodes > 200_000 ? 'nodes' : 'time', path.size) };
|
|
268
290
|
// Rows that read a term column only once a cut has put one on the path; before, the rows a choice-free search uses
|
|
269
291
|
// (what it saw before term columns), renumbered to the whole leaf's when a leaf is found.
|
|
270
292
|
const full = timed(() => rowsAt(goal, path), ms => { profile.rowsMs += ms; });
|
|
293
|
+
work.n += 1 + full.length;
|
|
271
294
|
const columns = [...path.values()].some(a => Object.keys(a.lin).some(key => key.startsWith('⟦')));
|
|
272
295
|
const idx = full.flatMap((r, i) => (columns || !Object.keys(r.lin).some(key => key.startsWith('⟦')) ? [i] : []));
|
|
273
296
|
const rows = idx.map(i => full[i]), at = (i) => (i < rows.length ? idx[i] : full.length + i - rows.length);
|
|
@@ -1362,16 +1385,14 @@ function proveLawsAsGiven(m, laws) {
|
|
|
1362
1385
|
focus: [...(law.forall ? [`${law.forall}#`] : []), ...(law.keys ?? [])],
|
|
1363
1386
|
});
|
|
1364
1387
|
});
|
|
1365
|
-
// (
|
|
1366
|
-
//
|
|
1388
|
+
// (by default a covered machine's certificate is its cyclic proof, core/cyclic.ts's certCyclic, the usual trees its
|
|
1389
|
+
// input, not checked again; where none is made, the usual trees are checked and the proof counted uncovered)
|
|
1367
1390
|
// (what no instance covers, read only where a cyclic mode is on: the usual route's own proofs, inferLaws' trial ones
|
|
1368
1391
|
// among them, do nothing more than before)
|
|
1369
1392
|
// A machine with a history: its laws are its plain machine's (historyMachine; the record goals leaf goals beside them).
|
|
1370
1393
|
const modes = cyclicOn() || certOn(), hist = modes && m.history ? historyMachine(m) : undefined, mc = hist ? hist.machine : m;
|
|
1371
|
-
const why = modes ? uncoveredWhy(mc, laws) : undefined;
|
|
1372
|
-
|
|
1373
|
-
const q = why === 'a flag at every key' && certOn() && certSearch(), cert = certOn() && (!why || q);
|
|
1374
|
-
usualCheck.on = !cert || q;
|
|
1394
|
+
const why = modes ? uncoveredWhy(mc, laws) : undefined, cert = certOn() && !why;
|
|
1395
|
+
usualCheck.on = !cert;
|
|
1375
1396
|
const t0 = performance.now(), out = (() => { try {
|
|
1376
1397
|
return proveThinned(m, laws, { ...instances });
|
|
1377
1398
|
}
|
|
@@ -1381,31 +1402,35 @@ function proveLawsAsGiven(m, laws) {
|
|
|
1381
1402
|
if (out.proof)
|
|
1382
1403
|
usualProof.last = { m, laws, proof: out.proof };
|
|
1383
1404
|
const inst = out.proof?.instances ?? instances;
|
|
1384
|
-
const searched = () => { const { facts, specs } =
|
|
1385
|
-
const translated =
|
|
1405
|
+
const searched = () => { const { facts, specs } = machineNodes(mc, laws, inst); return proveCyclic(facts, specs); };
|
|
1406
|
+
const translated = out.proof ? ((st) => translatedProof(() => JSON.stringify(['machine', mc, laws, inst]), () => timedKind('instance', 0, () => machineNodes(mc, laws, inst, out.proof, st)), st)) : undefined;
|
|
1386
1407
|
if (cyclicOn()) {
|
|
1387
|
-
|
|
1388
|
-
|
|
1408
|
+
const usual = out.proven && out.checked;
|
|
1409
|
+
// (a history: its plain machine's cyclic proof, and its record goals as the leaf goals they are; a machine with flags
|
|
1410
|
+
// that hold at every key the usual route doesn't prove: not searched, as before 0.19.0, a search there costs seconds)
|
|
1411
|
+
const records = !hist || hist.goals.every(g => { const tr = out.proof?.trees[g.name] ?? (() => { const o = grow(g); return 'tree' in o ? o.tree : undefined; })(); return tr !== undefined && check(g, tr); });
|
|
1412
|
+
const recordFails = () => ({ proven: false, checked: false, node: m.name, goal: 'a record goal', stuck: 'not proven' });
|
|
1413
|
+
if (!why && (usual || !hasQuantified(mc)))
|
|
1414
|
+
crossCyclic('machine', m.name, usual, records ? searched : recordFails, usualMs, records ? translated : undefined);
|
|
1389
1415
|
else
|
|
1390
|
-
cycLog({ kind: 'machine', name: m.name, usual
|
|
1416
|
+
cycLog({ kind: 'machine', name: m.name, usual, uncovered: true, why: why ?? 'a flag at every key the usual route does not prove' });
|
|
1391
1417
|
}
|
|
1392
1418
|
if (cert && out.proven && out.proof) {
|
|
1393
|
-
const
|
|
1394
|
-
if (
|
|
1395
|
-
uncovered('machine', m.name, `${why}: no cyclic proof found${performance.now() > until ? ` within ${certMs()} ms` : ''}`);
|
|
1396
|
-
else {
|
|
1419
|
+
const c = certCyclic('machine', m.name, translated, searched, usualMs);
|
|
1420
|
+
if (c.checked) {
|
|
1397
1421
|
// (a history's record goals checked as they are: leaf goals)
|
|
1398
|
-
|
|
1399
|
-
out.checked = c.checked && records;
|
|
1422
|
+
out.checked = !hist || hist.goals.every(g => out.proof.trees[g.name] !== undefined && check(g, out.proof.trees[g.name]));
|
|
1400
1423
|
Object.assign(out.proof, { cyclic: c.proof });
|
|
1401
1424
|
}
|
|
1425
|
+
else {
|
|
1426
|
+
out.checked = checkLaws(m, laws, out.proof);
|
|
1427
|
+
uncovered('machine', m.name, c.why);
|
|
1428
|
+
}
|
|
1402
1429
|
}
|
|
1403
1430
|
else if (certOn() && why && out.proven)
|
|
1404
1431
|
cycLog({ kind: 'machine', name: m.name, cert: true, uncovered: true, why });
|
|
1405
1432
|
return out;
|
|
1406
1433
|
}
|
|
1407
|
-
// A proof of a kind no cyclic instance covers, counted (`cyclic: {..., uncovered: true}`).
|
|
1408
|
-
const uncovered = (kind, name, why) => cycLog({ kind, name, uncovered: true, why });
|
|
1409
1434
|
// Whether the usual proof's trees are checked (off while GDP_CERT=cyclic makes the certificate a cyclic proof).
|
|
1410
1435
|
export const usualCheck = { on: true };
|
|
1411
1436
|
// The last machine proof made (core/dsl.ts's recurrences read their machine's, to translate it: core/cyclic.ts).
|
|
@@ -1545,38 +1570,52 @@ function proveStepLawsAsGiven(m, laws, stepLaws, base) {
|
|
|
1545
1570
|
leaves += countLeaves(out.tree);
|
|
1546
1571
|
}
|
|
1547
1572
|
const proof = { laws: base.proof, instances, ...(Object.keys(perStep).length ? { perStep } : {}), trees };
|
|
1548
|
-
const out = { proven: true, nodes, leaves, checked: checkStepLaws(m, laws, stepLaws, proof), proof };
|
|
1549
1573
|
// Step laws of a machine the cyclic instances cover (one step, or several over no map) are translated with the laws
|
|
1550
1574
|
// (core/cyclic.ts); the rest are counted as no instance's.
|
|
1551
|
-
|
|
1575
|
+
// (a machine with a history: its plain machine's; a flag at every key through its sk machine; k-step laws over a map
|
|
1576
|
+
// searched, where GDP_CERT_SEARCH=1; where no cyclic proof is made the usual trees, checked above, stay the certificate)
|
|
1577
|
+
const modes = cyclicOn() || certOn(), why = modes ? uncoveredWhy(plain, laws) : undefined, one = stepLaws;
|
|
1578
|
+
// Where the cyclic certificate is made below (GDP_CERT, not the cross-check; the laws' certificate checked), the usual
|
|
1579
|
+
// trees are its input, as a machine's laws' are: checkStepLaws (the laws' usual trees again, then the step laws') runs
|
|
1580
|
+
// only where no certificate is made.
|
|
1581
|
+
const byCert = certOn() && !cyclicOn() && !why && one.length > 0 && base.checked;
|
|
1582
|
+
const out = { proven: true, nodes, leaves, checked: byCert || checkStepLaws(m, laws, stepLaws, proof), proof };
|
|
1583
|
+
if (!modes)
|
|
1552
1584
|
return out;
|
|
1553
|
-
// (a machine with a history: its plain machine's; flags at every key and k-step laws over a map searched, where
|
|
1554
|
-
// GDP_CERT_SEARCH=1)
|
|
1555
|
-
const why = uncoveredWhy(plain, laws), one = stepLaws.filter(sl => (sl.steps ?? 1) === 1 || (!Object.keys(plain.maps ?? {}).length && !plain.inputMaps?.length));
|
|
1556
1585
|
if (!why && one.length && out.checked) {
|
|
1557
|
-
const
|
|
1558
|
-
const
|
|
1586
|
+
const all = one.map(sl => { const inst = perStep[sl.name] ?? instances, goal = stepLawObligations(m, laws, inst, sl)[0]; return { sl, inst, goal, tree: trees[goal.name] }; });
|
|
1587
|
+
const translatedOf = (steps) => (st) => translatedProof(() => JSON.stringify(['steps', plain, laws, base.proof.instances, steps.map(x => [x.sl, x.inst])]), reused => timedKind('instance', 0, () => machineNodes(plain, laws, base.proof.instances, { trees: base.proof.trees, steps, lawsMade: reused }, st)), st, () => JSON.stringify(['machine', plain, laws, base.proof.instances]));
|
|
1559
1588
|
const usualMs = performance.now() - t0;
|
|
1560
1589
|
if (cyclicOn())
|
|
1561
|
-
crossCyclic('step law', m.name, true, undefined, usualMs,
|
|
1562
|
-
|
|
1563
|
-
|
|
1590
|
+
crossCyclic('step law', m.name, true, undefined, usualMs, translatedOf(all));
|
|
1591
|
+
// (where the step laws' proof together isn't made, each step law's alone: one the translation misses leaves the
|
|
1592
|
+
// others theirs; and the usual trees, unchecked where the certificate was to be the verdict, are checked)
|
|
1593
|
+
const together = certOn() ? certCyclic('step law', m.name, translatedOf(all), undefined, usualMs) : undefined;
|
|
1594
|
+
if (together && !together.checked) {
|
|
1595
|
+
if (byCert)
|
|
1596
|
+
out.checked = checkStepLaws(m, laws, stepLaws, proof);
|
|
1597
|
+
for (const x of all) {
|
|
1598
|
+
const c = all.length > 1 ? certCyclic('step law', m.name, translatedOf([x]), undefined, usualMs) : together;
|
|
1599
|
+
if (!c.checked)
|
|
1600
|
+
uncovered((x.sl.steps ?? 1) > 1 ? 'k-step law' : 'step law', m.name, c.why);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1564
1603
|
}
|
|
1565
|
-
|
|
1566
|
-
|
|
1604
|
+
// (the rest: k steps over a map a request carries, or a machine no instance covers)
|
|
1605
|
+
const rest = stepLaws.filter(sl => why || !one.includes(sl)), restWhy = (sl) => why ?? `${sl.steps} steps over a map a request carries`;
|
|
1606
|
+
if (certOn() && certSearch() && out.checked && rest.length && !why) {
|
|
1567
1607
|
// The rest searched: the laws' nodes (searched), each step law's node (core/cyclic.ts's stepLawNodes).
|
|
1568
1608
|
const searchedSteps = () => {
|
|
1569
|
-
const b =
|
|
1609
|
+
const b = machineNodes(plain, laws, base.proof.instances);
|
|
1570
1610
|
return proveCyclic(b.facts, [...b.specs, ...searchedStepNodes(plain, laws, rest.map(sl => ({ law: sl, instances: perStep[sl.name] ?? instances })), b.facts, b.specs)]);
|
|
1571
1611
|
};
|
|
1572
|
-
const
|
|
1573
|
-
const none = `no cyclic proof found${performance.now() > until ? ` within ${certMs()} ms` : ''}`;
|
|
1612
|
+
const c = certCyclic('step law', m.name, undefined, searchedSteps, performance.now() - t0);
|
|
1574
1613
|
for (const sl of c.checked ? [] : rest)
|
|
1575
|
-
uncovered((sl.steps ?? 1) > 1 ? 'k-step law' : 'step law', m.name, `${
|
|
1614
|
+
uncovered((sl.steps ?? 1) > 1 ? 'k-step law' : 'step law', m.name, `${restWhy(sl)}: ${c.why}`);
|
|
1576
1615
|
}
|
|
1577
1616
|
else if (cyclicOn() || certOn())
|
|
1578
1617
|
for (const sl of rest)
|
|
1579
|
-
uncovered((sl.steps ?? 1) > 1 ? 'k-step law' : 'step law', m.name,
|
|
1618
|
+
uncovered((sl.steps ?? 1) > 1 ? 'k-step law' : 'step law', m.name, restWhy(sl));
|
|
1580
1619
|
return out;
|
|
1581
1620
|
}
|
|
1582
1621
|
// Progress (kernel.ts's progressObligations): the laws proven first, then each goal, the every-key laws assumed at every
|
|
@@ -1598,20 +1637,26 @@ export function proveProgress(m, laws, p) {
|
|
|
1598
1637
|
leaves += countLeaves(out.tree);
|
|
1599
1638
|
}
|
|
1600
1639
|
const checked = progressObligations(m, ls, instances, p).every(g => check(g, trees[g.name]));
|
|
1601
|
-
// (
|
|
1602
|
-
//
|
|
1603
|
-
// reachable state, every request meeting the region
|
|
1640
|
+
// (a request allowed whose claim is that alone, over a machine without a history or carried maps: the laws' nodes,
|
|
1641
|
+
// then the allowed goal translated, core/cyclic.ts's allowTranslated, its last node BridgeRules.lean's cycAOk: from
|
|
1642
|
+
// every reachable state, every request meeting the region and its terms is allowed; where GDP_CERT_SEARCH=1 and that
|
|
1643
|
+
// isn't proven, allowNode searched with the witnesses withWitness finds)
|
|
1604
1644
|
const alone = JSON.stringify(p.then) === JSON.stringify(trueC) && !m.history && !m.inputMaps?.length && m.assume !== undefined;
|
|
1605
|
-
if (certOn() &&
|
|
1606
|
-
const
|
|
1645
|
+
if (certOn() && alone && checked && base.proof) {
|
|
1646
|
+
const lp = base.proof, q = hasQuantified(m), goal = progressObligations(m, ls, instances, p)[0];
|
|
1647
|
+
const translated = (st) => translatedProof(() => JSON.stringify(['allowed', m, ls, lp.instances, instances, p]), reused => {
|
|
1648
|
+
const b = machineNodes(m, ls, lp.instances, { ...lp, lawsMade: reused }, st);
|
|
1649
|
+
if (b.why)
|
|
1650
|
+
return b;
|
|
1651
|
+
const a = allowTranslated(m, ls, instances, p, { goal, tree: trees[goal.name] }, b.facts, b.specs, `${m.name}: allowed`, st);
|
|
1652
|
+
return a.why ? { facts: b.facts, specs: [], why: a.why } : { facts: b.facts, specs: [...b.specs, ...a.specs] };
|
|
1653
|
+
}, st, () => JSON.stringify(['machine', m, ls, lp.instances]));
|
|
1607
1654
|
const allowed = () => {
|
|
1608
|
-
const b =
|
|
1655
|
+
const b = machineNodes(m, ls, lp.instances, lp);
|
|
1609
1656
|
if (b.why || (b.specs[0]?.rank.length ?? 2) < 2)
|
|
1610
1657
|
return { proven: false, checked: false, node: m.name, goal: 'allowed', stuck: b.why ?? 'laws ranked by time alone' };
|
|
1611
1658
|
let last = { proven: false, checked: false, node: m.name, goal: 'allowed', stuck: 'no witness' };
|
|
1612
1659
|
const found = withWitness(m, p.request, wit => {
|
|
1613
|
-
if (performance.now() > until)
|
|
1614
|
-
return undefined;
|
|
1615
1660
|
const w = { op: 'and', xs: [p.when, reqTS(Object.entries(p.request)), reqTS(wit)] };
|
|
1616
1661
|
const spec = allowNode(skMachine(m), ls, instances, w, b.facts, b.specs, `${m.name}: allowed`, q ? closingInsts(m, b.facts, [instances, ...Object.values(lp.instances)], [{ op: 'var', name: 't$' }], ls) : []);
|
|
1617
1662
|
last = proveCyclic(b.facts, [...b.specs, spec]);
|
|
@@ -1619,9 +1664,9 @@ export function proveProgress(m, laws, p) {
|
|
|
1619
1664
|
});
|
|
1620
1665
|
return found?.out ?? last;
|
|
1621
1666
|
};
|
|
1622
|
-
const c =
|
|
1667
|
+
const c = certCyclic('progress', m.name, translated, certSearch() ? allowed : undefined, performance.now() - t0);
|
|
1623
1668
|
if (!c.checked)
|
|
1624
|
-
uncovered('progress', m.name, `a request allowed:
|
|
1669
|
+
uncovered('progress', m.name, `a request allowed: ${c.why}`);
|
|
1625
1670
|
}
|
|
1626
1671
|
else if (cyclicOn() || certOn())
|
|
1627
1672
|
uncovered('progress', m.name, alone ? 'a request allowed' : 'a request allowed and a claim after it');
|