gdp-wired 0.3.0 → 0.5.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 +109 -113
- package/dist/core/dsl.d.ts +210 -9
- package/dist/core/dsl.js +799 -78
- package/dist/core/watch.d.ts +14 -0
- package/dist/core/watch.js +78 -0
- package/dist/kernel/kernel.d.ts +7 -0
- package/dist/kernel/kernel.js +121 -11
- package/dist/kernel/prove.d.ts +3 -0
- package/dist/kernel/prove.js +190 -51
- package/dist/lean-kernel/emit.d.ts +2 -10
- package/dist/lean-kernel/emit.js +164 -201
- package/dist/lean-kernel/library.js +5 -5
- package/package.json +2 -2
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Law, type Machine, type Request, type State, type interpret } from './machine.js';
|
|
2
|
+
import type { StepLaw } from '../kernel/kernel.js';
|
|
3
|
+
export type Watched = Law | StepLaw;
|
|
4
|
+
export declare const isStepLaw: (w: Watched) => w is StepLaw;
|
|
5
|
+
type Runtime = ReturnType<typeof interpret>;
|
|
6
|
+
type Env = Record<string, number>;
|
|
7
|
+
export declare function requestEnv(m: Machine, run: Runtime, params: Env, st: State, input: Request): Env;
|
|
8
|
+
export declare const allowed: (m: Machine, run: Runtime, params: Env, st: State, input: Request) => boolean;
|
|
9
|
+
export declare const keysOf: (bound: number, ...states: State[]) => number[];
|
|
10
|
+
export declare function watch(m: Machine, run: Runtime, w: Watched, params: Env, bound: number): {
|
|
11
|
+
start: (st: State) => boolean;
|
|
12
|
+
step: (_before: State, _input: Request, after: State) => boolean;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// A law or a step law read off one run as it happens: the concrete side of kernel.ts's goals. The same description
|
|
2
|
+
// (a Law, or a StepLaw with its `steps`, `when` and `then`) is proven about every run by the kernel, searched for a
|
|
3
|
+
// breaking run by Handwired (research/hw/compile.ts), and read here on one run: the examples' random runs (examples/harness.ts),
|
|
4
|
+
// the replay of every counterexample the search finds, and the reference the Handwired search is checked against.
|
|
5
|
+
//
|
|
6
|
+
// A law holds at the start and after every step (at every key, for a law about every key). A step law over k steps holds
|
|
7
|
+
// at every k steps in a row the run takes: each step's `when` read with the state it starts from, its request, queries
|
|
8
|
+
// and flags; `then` with the state before the first step (plain names), the first request and its flags, the j-th
|
|
9
|
+
// request as input@j, and the state and maps after the last step (primed names).
|
|
10
|
+
import { carriedView, evalC, numbersOf } from "./machine.js";
|
|
11
|
+
export const isStepLaw = (w) => 'then' in w;
|
|
12
|
+
// A request's environment: the params, its inputs and queries, and the step's flags decided (as ?name).
|
|
13
|
+
export function requestEnv(m, run, params, st, input) {
|
|
14
|
+
const env = { ...params, ...numbersOf(input), ...run.queries(params, st, input) }, seen = carriedView(m, st, input);
|
|
15
|
+
for (const [name, c] of Object.entries(m.step.flags ?? {}))
|
|
16
|
+
env[`?${name}`] = evalC(c, env, seen, m) ? 1 : 0;
|
|
17
|
+
return env;
|
|
18
|
+
}
|
|
19
|
+
// Whether the machine allows the request: its assumption, read with the queries and flags.
|
|
20
|
+
// A request whose order goes back (with a pruned history) is one the runtimes refuse.
|
|
21
|
+
export const allowed = (m, run, params, st, input) => {
|
|
22
|
+
const order = m.history?.prune?.order;
|
|
23
|
+
if (order !== undefined && st.last !== undefined && input[order] < st.last)
|
|
24
|
+
return false;
|
|
25
|
+
return !m.assume || evalC(m.assume, requestEnv(m, run, params, st, input), carriedView(m, st, input), m);
|
|
26
|
+
};
|
|
27
|
+
// The keys a law about every key is read at: 0 to bound + 1, and every key a map holds.
|
|
28
|
+
export const keysOf = (bound, ...states) => {
|
|
29
|
+
const keys = new Set(Array.from({ length: bound + 2 }, (_, i) => i));
|
|
30
|
+
for (const st of states)
|
|
31
|
+
for (const cells of Object.values(st.maps))
|
|
32
|
+
for (const key of cells.keys())
|
|
33
|
+
keys.add(key);
|
|
34
|
+
return [...keys];
|
|
35
|
+
};
|
|
36
|
+
// Watches one law or step law along one run: `start` with the state at the start, `step` after each step the run takes
|
|
37
|
+
// (the state before, the request, the state after). Each says whether the law still holds.
|
|
38
|
+
export function watch(m, run, w, params, bound) {
|
|
39
|
+
// At every tuple of the keys, one key per name.
|
|
40
|
+
const atKeys = (forall, keys, f, more = []) => {
|
|
41
|
+
const names = forall ? [forall, ...more] : [];
|
|
42
|
+
const go = (i, env) => (i === names.length ? f(env) : keys.every(key => go(i + 1, { ...env, [names[i]]: key })));
|
|
43
|
+
return go(0, {});
|
|
44
|
+
};
|
|
45
|
+
if (!isStepLaw(w)) {
|
|
46
|
+
const holds = (st) => atKeys(w.forall, keysOf(bound, st), extra => evalC(w.holds, { ...params, ...extra }, st, m), w.keys);
|
|
47
|
+
return { start: holds, step: (_before, _input, after) => holds(after) };
|
|
48
|
+
}
|
|
49
|
+
const k = w.steps ?? 1, whens = w.when === undefined ? [] : Array.isArray(w.when) ? w.when : [w.when];
|
|
50
|
+
// `then` reads a primed map's cells and initial value as its own map's.
|
|
51
|
+
const primedMaps = { ...m, maps: { ...m.maps, ...Object.fromEntries(Object.entries(m.maps ?? {}).map(([n, init]) => [`${n}'`, init])) } };
|
|
52
|
+
const window = [];
|
|
53
|
+
return {
|
|
54
|
+
start: (_st) => true,
|
|
55
|
+
step: (before, input, after) => {
|
|
56
|
+
window.push({ before, input, after });
|
|
57
|
+
if (window.length > k)
|
|
58
|
+
window.shift();
|
|
59
|
+
if (window.length < k)
|
|
60
|
+
return true;
|
|
61
|
+
const envs = window.map(t => requestEnv(m, run, params, t.before, t.input));
|
|
62
|
+
if (whens.some((c, j) => !evalC(c, envs[j], carriedView(m, window[j].before, window[j].input), m)))
|
|
63
|
+
return true;
|
|
64
|
+
const env = { ...envs[0] };
|
|
65
|
+
window.slice(1).forEach((t, j) => { for (const i of m.inputs)
|
|
66
|
+
env[`${i}@${j + 2}`] = envs[j + 1][i]; });
|
|
67
|
+
// The first state with the first request's maps; each later request's maps as map@j; the last state primed.
|
|
68
|
+
let first = carriedView(m, window[0].before, window[0].input);
|
|
69
|
+
window.slice(1).forEach((t, j) => { first = carriedView(m, first, t.input, map => `${map}@${j + 2}`); });
|
|
70
|
+
const both = {
|
|
71
|
+
scalars: { ...first.scalars, ...Object.fromEntries(Object.entries(after.scalars).map(([n, v]) => [`${n}'`, v])) },
|
|
72
|
+
maps: { ...first.maps, ...Object.fromEntries(Object.entries(after.maps).map(([n, cells]) => [`${n}'`, cells])) },
|
|
73
|
+
defaults: { ...first.defaults, ...Object.fromEntries(Object.entries(after.defaults ?? {}).map(([n, d]) => [`${n}'`, d])) },
|
|
74
|
+
};
|
|
75
|
+
return atKeys(w.forall, keysOf(bound, first, after), extra => evalC(w.then, { ...env, ...extra }, both, primedMaps), w.keys);
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
package/dist/kernel/kernel.d.ts
CHANGED
|
@@ -20,6 +20,10 @@ export type Path = Map<string, {
|
|
|
20
20
|
export declare const canon: (lin: Lin) => string;
|
|
21
21
|
export declare const atomOf: (lin: Lin, holds: boolean) => Constraint;
|
|
22
22
|
export declare function linear(e: E, path: Path): Lin | undefined;
|
|
23
|
+
export declare const termColumn: (e: E) => string;
|
|
24
|
+
export declare const columnTerm: (name: string) => E | undefined;
|
|
25
|
+
export declare function decided(e: E, path: Path): boolean;
|
|
26
|
+
export declare function termRows(name: string, path: Path): Lin[];
|
|
23
27
|
export declare function truth(c: C, path: Path): boolean | undefined;
|
|
24
28
|
export declare function facts(c: C, path: Path, positive?: boolean): Lin[];
|
|
25
29
|
export declare function leafConstraints(goal: Goal, path: Path): Constraint[];
|
|
@@ -33,6 +37,8 @@ export type Subst = {
|
|
|
33
37
|
export declare function substE(e: E, s: Subst): E;
|
|
34
38
|
export declare function substC(c: C, s: Subst): C;
|
|
35
39
|
export type Instances = Record<string, E[]>;
|
|
40
|
+
export declare const stateName: (n: string, j: number) => string;
|
|
41
|
+
export declare const assumed: (laws: Law[], instances: Instances) => C[];
|
|
36
42
|
export declare function scope(m: Machine, laws?: Law[]): void;
|
|
37
43
|
export declare const keyTerm: (e: E, key: string) => boolean;
|
|
38
44
|
export declare const plainKey: (t: E | C, key: string) => boolean;
|
|
@@ -103,6 +109,7 @@ export declare function checkStepLaws(m: Machine, laws: Law[], stepLaws: StepLaw
|
|
|
103
109
|
perStep?: Record<string, Instances>;
|
|
104
110
|
trees: Record<string, Tree>;
|
|
105
111
|
}): boolean;
|
|
112
|
+
export declare const stepZone: (m: Machine, k: number) => string[];
|
|
106
113
|
export type Fold = {
|
|
107
114
|
name: string;
|
|
108
115
|
params: string[];
|
package/dist/kernel/kernel.js
CHANGED
|
@@ -35,11 +35,24 @@ function decide(lin, path) {
|
|
|
35
35
|
const flipped = path.get(canon(add(k(1), scale(lin, -1))))?.holds;
|
|
36
36
|
return flipped === undefined ? undefined : !flipped;
|
|
37
37
|
}
|
|
38
|
-
// A term's linear form under the path,
|
|
39
|
-
//
|
|
38
|
+
// A term's linear form under the path. Reads become cells, named by map and key: `M[key]`; a choice the path leaves
|
|
39
|
+
// open (∸, min, max, if) becomes a column of its own, `⟦n⟧`, standing for the term's value (see termRows).
|
|
40
|
+
// Remembered per path and term (a path is never changed once built; a new decision is a new path), so a term shared
|
|
41
|
+
// across hypotheses, or nested in a k-step run's later states, is read once per path.
|
|
42
|
+
const linearMemo = new WeakMap();
|
|
40
43
|
export function linear(e, path) {
|
|
44
|
+
let memo = linearMemo.get(path);
|
|
45
|
+
if (!memo)
|
|
46
|
+
linearMemo.set(path, (memo = new WeakMap()));
|
|
47
|
+
if (memo.has(e))
|
|
48
|
+
return memo.get(e);
|
|
49
|
+
const out = linearOf(e, path);
|
|
50
|
+
memo.set(e, out);
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
function linearOf(e, path) {
|
|
41
54
|
switch (e.op) {
|
|
42
|
-
case 'var': return e.name === '1' || /[*[\]
|
|
55
|
+
case 'var': return e.name === '1' || /[*[\],:⟦]/.test(e.name) ? undefined : v(e.name); // those characters build cells, monomials and term columns
|
|
43
56
|
case 'const': return k(e.value);
|
|
44
57
|
case 'mul': {
|
|
45
58
|
const a = linear(e.a, path);
|
|
@@ -61,14 +74,14 @@ export function linear(e, path) {
|
|
|
61
74
|
return undefined;
|
|
62
75
|
const aLeB = decide(sub(a, b), path), bLeA = decide(sub(b, a), path);
|
|
63
76
|
if (e.op === 'sub')
|
|
64
|
-
return bLeA === true ? sub(a, b) : bLeA === false ? k(0) :
|
|
77
|
+
return bLeA === true ? sub(a, b) : bLeA === false ? k(0) : v(termColumn(e));
|
|
65
78
|
if (aLeB === undefined)
|
|
66
|
-
return
|
|
79
|
+
return v(termColumn(e));
|
|
67
80
|
return (e.op === 'min') === aLeB ? a : b;
|
|
68
81
|
}
|
|
69
82
|
case 'if': {
|
|
70
83
|
const c = truth(e.c, path);
|
|
71
|
-
return c === undefined ?
|
|
84
|
+
return c === undefined ? v(termColumn(e)) : linear(c ? e.then : e.else, path);
|
|
72
85
|
}
|
|
73
86
|
case 'get': {
|
|
74
87
|
const key = linear(e.key, path);
|
|
@@ -76,6 +89,61 @@ export function linear(e, path) {
|
|
|
76
89
|
}
|
|
77
90
|
}
|
|
78
91
|
}
|
|
92
|
+
// --- term columns -------------------------------------------------------------------------------------------------------
|
|
93
|
+
// A choice the path leaves open is a column ⟦n⟧ for the n-th term met (one table per process: the name only has to be
|
|
94
|
+
// the same for the same term). A leaf reads it through rows that hold whichever way the choice goes (termRows), so a
|
|
95
|
+
// fact about the choice's value can be split on, and proven, before the choice is decided.
|
|
96
|
+
// Terms are hash-consed: a term's id comes from its operator, its fields and its parts' ids, remembered per object, so a
|
|
97
|
+
// deep term (a k-step run nests k steps) is read once, not once per lookup.
|
|
98
|
+
const termIds = new Map(), termTable = [], memo = new WeakMap();
|
|
99
|
+
function termId(t) {
|
|
100
|
+
const had = memo.get(t);
|
|
101
|
+
if (had !== undefined)
|
|
102
|
+
return had;
|
|
103
|
+
const parts = Object.entries(t).sort(([x], [y]) => (x < y ? -1 : x > y ? 1 : 0)).map(([key, val]) => (Array.isArray(val) ? `${key}=[${val.map(x => termId(x)).join(',')}]` : typeof val === 'object' && val !== null ? `${key}=#${termId(val)}` : `${key}=${JSON.stringify(val)}`));
|
|
104
|
+
const key = parts.join(';');
|
|
105
|
+
let id = termIds.get(key);
|
|
106
|
+
if (id === undefined) {
|
|
107
|
+
id = termTable.length;
|
|
108
|
+
termIds.set(key, id);
|
|
109
|
+
termTable.push(t);
|
|
110
|
+
}
|
|
111
|
+
memo.set(t, id);
|
|
112
|
+
return id;
|
|
113
|
+
}
|
|
114
|
+
export const termColumn = (e) => `⟦${termId(e)}⟧`;
|
|
115
|
+
export const columnTerm = (name) => { const m = /^⟦(\d+)⟧$/.exec(name); return m ? termTable[Number(m[1])] : undefined; };
|
|
116
|
+
// Whether the path decides a choice's top (an if's condition, which side of min, max or ∸).
|
|
117
|
+
export function decided(e, path) {
|
|
118
|
+
if (e.op === 'if')
|
|
119
|
+
return truth(e.c, path) !== undefined;
|
|
120
|
+
if (e.op !== 'sub' && e.op !== 'min' && e.op !== 'max')
|
|
121
|
+
return true;
|
|
122
|
+
const a = linear(e.a, path), b = linear(e.b, path);
|
|
123
|
+
return !!a && !!b && decide(e.op === 'sub' ? sub(b, a) : sub(a, b), path) !== undefined;
|
|
124
|
+
}
|
|
125
|
+
// The rows a term column brings: its bounds, true whichever way the choice goes (min(a, b) ≤ a and ≤ b; max(a, b) ≥ a
|
|
126
|
+
// and ≥ b; a ∸ b ≥ a − b and ≤ a), and, once the path decides it, its equality to the branch taken.
|
|
127
|
+
export function termRows(name, path) {
|
|
128
|
+
const e = columnTerm(name), col = v(name), out = [];
|
|
129
|
+
if (e.op === 'min' || e.op === 'max' || e.op === 'sub') {
|
|
130
|
+
const a = linear(e.a, path), b = linear(e.b, path);
|
|
131
|
+
if (a && b) {
|
|
132
|
+
if (e.op === 'min')
|
|
133
|
+
out.push(sub(col, a), sub(col, b));
|
|
134
|
+
if (e.op === 'max')
|
|
135
|
+
out.push(sub(a, col), sub(b, col));
|
|
136
|
+
if (e.op === 'sub')
|
|
137
|
+
out.push(sub(sub(a, b), col), sub(col, a));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (decided(e, path)) {
|
|
141
|
+
const r = linear(e, path);
|
|
142
|
+
if (r)
|
|
143
|
+
out.push(sub(col, r), sub(r, col));
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
79
147
|
// Products: multiply out, each monomial (variables joined by '*', sorted) one more natural variable. Forgetting how
|
|
80
148
|
// monomials relate (x*x to x, say) only weakens what a leaf may use, so it stays sound; distributing is what proves
|
|
81
149
|
// spend <= m·count over a node from the same law at its children. Map reads can't be factors.
|
|
@@ -83,8 +151,8 @@ function times(a, b) {
|
|
|
83
151
|
const out = [];
|
|
84
152
|
for (const [p, cp] of Object.entries(a))
|
|
85
153
|
for (const [q, cq] of Object.entries(b)) {
|
|
86
|
-
if (p.includes('[') || q.includes('['))
|
|
87
|
-
return undefined;
|
|
154
|
+
if (p.includes('[') || q.includes('[') || p.includes('⟦') || q.includes('⟦'))
|
|
155
|
+
return undefined; // factors are variables (and monomials of them)
|
|
88
156
|
const factors = [...(p === '1' ? [] : p.split('*')), ...(q === '1' ? [] : q.split('*'))].sort();
|
|
89
157
|
out.push({ [factors.length ? factors.join('*') : '1']: cp * cq });
|
|
90
158
|
}
|
|
@@ -172,6 +240,23 @@ export function leafConstraints(goal, path) {
|
|
|
172
240
|
if (decide(d, path) === true && decide(scale(d, -1), path) === true)
|
|
173
241
|
rows.push({ lin: sub(v(p.name), v(q.name)), label: '' }, { lin: sub(v(q.name), v(p.name)), label: '' });
|
|
174
242
|
}
|
|
243
|
+
// Each term column's rows, and the columns those rows bring in turn.
|
|
244
|
+
const seen = new Set(), todo = [...names].filter(n => n.startsWith('⟦'));
|
|
245
|
+
while (todo.length) {
|
|
246
|
+
const name = todo.pop();
|
|
247
|
+
if (seen.has(name))
|
|
248
|
+
continue;
|
|
249
|
+
seen.add(name);
|
|
250
|
+
for (const lin of termRows(name, path)) {
|
|
251
|
+
rows.push({ lin, label: '' });
|
|
252
|
+
for (const key of Object.keys(lin))
|
|
253
|
+
if (key !== '1') {
|
|
254
|
+
names.add(key);
|
|
255
|
+
if (key.startsWith('⟦') && !seen.has(key))
|
|
256
|
+
todo.push(key);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
175
260
|
for (const name of names)
|
|
176
261
|
rows.push({ lin: { [name]: -1 }, label: '' });
|
|
177
262
|
return rows;
|
|
@@ -300,6 +385,14 @@ const step = (r, u) => {
|
|
|
300
385
|
flags: at.flags,
|
|
301
386
|
} };
|
|
302
387
|
};
|
|
388
|
+
// The state after a step, named: each state variable a fresh name n~j equal to its term there, which later frames read,
|
|
389
|
+
// so a run of k steps is k small frames rather than a term nesting each state in the next (which grows as 3^k when a
|
|
390
|
+
// step reads its state thrice). The names are new to the goal; each equation holds of the run that took the steps.
|
|
391
|
+
const nameState = (r, state, j) => ({
|
|
392
|
+
hyps: [...r.hyps, ...state.map(n => ({ op: '=', a: { op: 'var', name: stateName(n, j) }, b: r.at.vars[n] ?? { op: 'var', name: n } }))],
|
|
393
|
+
at: { ...r.at, vars: { ...r.at.vars, ...Object.fromEntries(state.map(n => [n, { op: 'var', name: stateName(n, j) }])) } },
|
|
394
|
+
});
|
|
395
|
+
export const stateName = (n, j) => `${n}~${j}`;
|
|
303
396
|
// The conclusion read at the run's end: plain names at the last frame.
|
|
304
397
|
const atEnd = (r, c) => substC(c, r.at);
|
|
305
398
|
// The conclusion read across the run: plain names at the first frame, primed names (s', and M'[k] as a read of the map
|
|
@@ -344,7 +437,7 @@ const laterNames = (m, j) => ({
|
|
|
344
437
|
maps: Object.fromEntries((m.inputMaps ?? []).map(map => [map, `${map}@${j}`])),
|
|
345
438
|
});
|
|
346
439
|
// The laws assumed where the run is: each plain law, and each every-key law at the keys the proof names.
|
|
347
|
-
const assumed = (laws, instances) => laws.flatMap(l => {
|
|
440
|
+
export const assumed = (laws, instances) => laws.flatMap(l => {
|
|
348
441
|
const keys = instances[l.name] ?? [];
|
|
349
442
|
if (l.keys?.length) {
|
|
350
443
|
const names = [l.forall, ...l.keys], n = names.length;
|
|
@@ -799,8 +892,9 @@ export function stepLawObligations(machine, laws, instances, sl) {
|
|
|
799
892
|
const primes = [...state, ...maps].map(n => `${n}'`);
|
|
800
893
|
const laters = Array.from({ length: k - 1 }, (_, i) => laterNames(m, i + 2));
|
|
801
894
|
const later = laters.flatMap(l => Object.values(l.vars).map(e => e.name)), laterMaps = laters.flatMap(l => Object.values(l.maps));
|
|
895
|
+
const names = Array.from({ length: k - 1 }, (_, i) => state.map(n => stateName(n, i + 1))).flat();
|
|
802
896
|
const taken = [...m.params, ...m.inputs, ...state, ...maps, ...carried];
|
|
803
|
-
for (const p of [...primes, ...later, ...laterMaps])
|
|
897
|
+
for (const p of [...primes, ...later, ...laterMaps, ...names])
|
|
804
898
|
if (taken.includes(p))
|
|
805
899
|
throw new Error(`${m.name}: ${p} is a name of the machine and a name a step law reads`);
|
|
806
900
|
for (const key of [sl.forall, ...(sl.keys ?? [])])
|
|
@@ -822,14 +916,30 @@ export function stepLawObligations(machine, laws, instances, sl) {
|
|
|
822
916
|
if (j > 1)
|
|
823
917
|
run = reading(run, laters[j - 2].vars, laters[j - 2].maps);
|
|
824
918
|
run = step(holds(run, [...assumed(laws, instances), ...assume, ...(whens[j - 1] ? [inlineFlags(m, whens[j - 1])] : []), ...closing(m, instances)]), u);
|
|
919
|
+
if (j < k)
|
|
920
|
+
run = nameState(run, state, j);
|
|
825
921
|
}
|
|
826
922
|
return [goal(k === 1 ? `${sl.name} @step law` : `${sl.name} @${k} steps`, run, across(run, sl.then, state, maps, { inputs: {}, flags: flagsOf(m) }))];
|
|
827
923
|
}
|
|
828
924
|
// The laws' proof and every step law's tree: all must check.
|
|
829
925
|
// `perStep`: a step law's own instances, where it has them (else `instances`).
|
|
926
|
+
// Each frame's hypotheses read no name a later step's request is read by: the condition the Lean twin's proof needs
|
|
927
|
+
// (Sets.lean's checkSKStep, `avoids (szone s sl)`; plain flags are inlined here, so their names never occur), checked
|
|
928
|
+
// here alike.
|
|
830
929
|
export function checkStepLaws(m, laws, stepLaws, proof) {
|
|
831
|
-
return checkLaws(m, laws, proof.laws) && stepLaws.every(sl =>
|
|
930
|
+
return checkLaws(m, laws, proof.laws) && stepLaws.every(sl => {
|
|
931
|
+
const inst = proof.perStep?.[sl.name] ?? proof.instances, plain = m.history ? historyMachine(m).machine : m;
|
|
932
|
+
const zone = stepZone(plain, sl.steps ?? 1);
|
|
933
|
+
return [...assumed(laws, inst), ...assumed(plain.facts ?? [], inst), ...closing(plain, inst)].every(c => !mentions(c, zone))
|
|
934
|
+
&& stepLawObligations(m, laws, inst, sl).every(g => proof.trees[g.name] !== undefined && check(g, proof.trees[g.name]));
|
|
935
|
+
});
|
|
832
936
|
}
|
|
937
|
+
// The names a k-step law's run introduces past its first frame: each later step's inputs, quantified flags and their
|
|
938
|
+
// witnesses, and the state after each step but the last (stateName).
|
|
939
|
+
export const stepZone = (m, k) => [
|
|
940
|
+
...Array.from({ length: k - 1 }, (_, i) => Object.values(laterNames(m, i + 2).vars).map(e => e.name)).flat(),
|
|
941
|
+
...Array.from({ length: k - 1 }, (_, i) => Object.keys(m.state).map(n => stateName(n, i + 1))).flat(),
|
|
942
|
+
];
|
|
833
943
|
export function foldObligations(f, laws) {
|
|
834
944
|
if (![...Object.values(f.leaf), ...Object.values(f.node)].every(natural) || !laws.every(law => !law.forall && !law.keys?.length && naturalC(law.holds) && !mentions(law.holds, f.inputs)))
|
|
835
945
|
throw new Error('outside the kernel');
|
package/dist/kernel/prove.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export type Outcome = {
|
|
|
7
7
|
stuck: 'sat' | 'unknown';
|
|
8
8
|
witness?: Record<string, number>;
|
|
9
9
|
path: Path;
|
|
10
|
+
why?: string;
|
|
10
11
|
};
|
|
11
12
|
export declare function grow(goal: Goal, path?: Path, budget?: {
|
|
12
13
|
nodes: number;
|
|
@@ -20,6 +21,7 @@ export type Report = {
|
|
|
20
21
|
goal: string;
|
|
21
22
|
stuck: string;
|
|
22
23
|
witness?: Record<string, number>;
|
|
24
|
+
why?: string;
|
|
23
25
|
};
|
|
24
26
|
checked: boolean;
|
|
25
27
|
};
|
|
@@ -58,6 +60,7 @@ export declare function proveLiveness(m: Machine, laws: Law[], l: Liveness): Rep
|
|
|
58
60
|
};
|
|
59
61
|
export declare const countLeaves: (t: Tree) => number;
|
|
60
62
|
export declare function concretize(goal: Goal, bound?: number): Record<string, number> | undefined;
|
|
63
|
+
export declare function factsFor(m: Machine, goal: Goal): C[];
|
|
61
64
|
export declare function proveBuilt(m: Machine, laws: Law[], build: (inst: Instances) => Goal): {
|
|
62
65
|
proven: boolean;
|
|
63
66
|
instances?: Instances;
|