gdp-wired 0.2.0 → 0.3.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 +75 -32
- package/dist/core/compose.d.ts +15 -1
- package/dist/core/compose.js +132 -8
- package/dist/core/dsl.d.ts +84 -13
- package/dist/core/dsl.js +380 -23
- package/dist/core/machine.d.ts +34 -6
- package/dist/core/machine.js +194 -22
- package/dist/kernel/kernel.d.ts +29 -2
- package/dist/kernel/kernel.js +306 -48
- package/dist/kernel/prove.d.ts +33 -9
- package/dist/kernel/prove.js +298 -24
- package/dist/lean-kernel/emit.d.ts +132 -0
- package/dist/lean-kernel/emit.js +936 -0
- package/dist/lean-kernel/lean-terms.d.ts +5 -0
- package/dist/lean-kernel/lean-terms.js +44 -0
- package/dist/lean-kernel/library.d.ts +1 -0
- package/dist/lean-kernel/library.js +13 -0
- package/package.json +11 -4
package/dist/core/machine.js
CHANGED
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
// Arithmetic is linear: +, −, × by a constant, min, max, if. Maps are naturals indexed by naturals, every cell starting
|
|
7
7
|
// at the same value; a law about a map holds for every key x, and is proven at one arbitrary x (with the law assumed at
|
|
8
8
|
// every other key the step reads).
|
|
9
|
-
import { historyMachine, scope } from "../kernel/kernel.js";
|
|
9
|
+
import { historyMachine, keyTerm, plainKey, scope } from "../kernel/kernel.js";
|
|
10
10
|
import { add, eq, k, le, lt, nat, scale, show, solve, sub, v } from "./solver.js";
|
|
11
|
+
// A write at one key, for the parts that take no write at every key (the symbolic prover, the GPU synthesizers, the
|
|
12
|
+
// hand-written shader, the first Lean export): they refuse one by name.
|
|
13
|
+
export const pointWrite = (w, where) => { if ('each' in w)
|
|
14
|
+
throw new Error(`${where} takes no write at every key`); return w; };
|
|
15
|
+
// The same for a quantified flag.
|
|
16
|
+
export const noQuantifier = (where) => { throw new Error(`${where} takes no quantified flag`); };
|
|
11
17
|
export { unreachable } from "../kernel/kernel.js";
|
|
12
18
|
// Builders.
|
|
13
19
|
export const x = (name) => ({ op: 'var', name });
|
|
@@ -27,6 +33,76 @@ export const all = (...xs) => ({ op: 'and', xs });
|
|
|
27
33
|
export const any = (...xs) => ({ op: 'or', xs });
|
|
28
34
|
export const not = (c) => ({ op: 'not', x: c });
|
|
29
35
|
export const bit = (name) => any(same(x(name), n(0)), same(x(name), n(1)));
|
|
36
|
+
// A condition at every key, or a value at every key. Where the bound key is read only as a map's key, each key a map
|
|
37
|
+
// read holds a cell at, and one key past them all (where every map reads its default), are every case there is.
|
|
38
|
+
const mapsIn = (t) => [...new Set([...JSON.stringify(t).matchAll(/"op":"get","map":"([^"]+)"/g)].map(x => x[1]))];
|
|
39
|
+
function keysAt(st, maps) {
|
|
40
|
+
const keys = new Set();
|
|
41
|
+
let past = 0;
|
|
42
|
+
for (const cells of Object.values(st.maps))
|
|
43
|
+
for (const key of cells.keys())
|
|
44
|
+
past = Math.max(past, key + 1);
|
|
45
|
+
for (const map of maps)
|
|
46
|
+
for (const key of st.maps[map]?.keys() ?? [])
|
|
47
|
+
keys.add(key);
|
|
48
|
+
return [...keys, past];
|
|
49
|
+
}
|
|
50
|
+
const breaksCache = new WeakMap();
|
|
51
|
+
function breaksOf(t, key) {
|
|
52
|
+
const hit = breaksCache.get(t);
|
|
53
|
+
if (hit)
|
|
54
|
+
return hit;
|
|
55
|
+
const out = { reads: [], cmps: [] };
|
|
56
|
+
const termOf = (e) => (e.op === 'var' ? { minus: false } : e.op === 'add' ? { off: e.a.op === 'var' && e.a.name === key ? e.b : e.a, minus: false } : { off: e.b, minus: true });
|
|
57
|
+
const walk = (u) => {
|
|
58
|
+
if (u.op === 'get' && keyTerm(u.key, key)) {
|
|
59
|
+
out.reads.push({ map: u.map, ...termOf(u.key) });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if ((u.op === '<' || u.op === '<=' || u.op === '=') && (keyTerm(u.a, key) || keyTerm(u.b, key))) {
|
|
63
|
+
const [kt, other] = keyTerm(u.a, key) ? [u.a, u.b] : [u.b, u.a];
|
|
64
|
+
out.cmps.push({ value: other, ...termOf(kt) });
|
|
65
|
+
walk(other);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
for (const v of Object.values(u))
|
|
69
|
+
if (v && typeof v === 'object')
|
|
70
|
+
(Array.isArray(v) ? v : [v]).forEach(y => walk(y));
|
|
71
|
+
};
|
|
72
|
+
walk(t);
|
|
73
|
+
breaksCache.set(t, out);
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
function breakKeys(b, env, st, m) {
|
|
77
|
+
const points = new Set([0]);
|
|
78
|
+
for (const r of b.reads) {
|
|
79
|
+
const d = r.off ? evalE(r.off, env, st, m) : 0;
|
|
80
|
+
for (const s of st.maps[r.map]?.keys() ?? []) {
|
|
81
|
+
if (r.minus)
|
|
82
|
+
points.add(s + d);
|
|
83
|
+
else if (s >= d)
|
|
84
|
+
points.add(s - d);
|
|
85
|
+
}
|
|
86
|
+
if (r.minus)
|
|
87
|
+
points.add(d);
|
|
88
|
+
}
|
|
89
|
+
for (const c of b.cmps) {
|
|
90
|
+
const v = evalE(c.value, env, st, m), d = c.off ? evalE(c.off, env, st, m) : 0;
|
|
91
|
+
if (c.minus) {
|
|
92
|
+
points.add(v + d);
|
|
93
|
+
points.add(d);
|
|
94
|
+
}
|
|
95
|
+
else if (v >= d)
|
|
96
|
+
points.add(v - d);
|
|
97
|
+
}
|
|
98
|
+
const keys = new Set();
|
|
99
|
+
for (const p of points)
|
|
100
|
+
for (let i = -1; i <= 2; i++)
|
|
101
|
+
if (p + i >= 0)
|
|
102
|
+
keys.add(p + i);
|
|
103
|
+
const sorted = [...keys].sort((a, b) => a - b);
|
|
104
|
+
return [...sorted, sorted.at(-1) + 1];
|
|
105
|
+
}
|
|
30
106
|
export const naturalIn = (v, what) => {
|
|
31
107
|
if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0)
|
|
32
108
|
throw new RangeError(`${what} must be a natural below 2^53, not ${String(v)}`);
|
|
@@ -53,9 +129,24 @@ export function evalE(e, env, st, m) {
|
|
|
53
129
|
case 'min': return Math.min(evalE(e.a, env, st, m), evalE(e.b, env, st, m));
|
|
54
130
|
case 'max': return Math.max(evalE(e.a, env, st, m), evalE(e.b, env, st, m));
|
|
55
131
|
case 'if': return evalC(e.c, env, st, m) ? evalE(e.then, env, st, m) : evalE(e.else, env, st, m);
|
|
56
|
-
case 'get':
|
|
132
|
+
case 'get': {
|
|
133
|
+
const key = evalE(e.key, env, st, m), fn = m.fns?.[e.map];
|
|
134
|
+
return fn ? naturalIn(fnOf(fn)(key), `${e.map}(${key})`) : st.maps[e.map].get(key) ?? st.defaults?.[e.map] ?? m.maps[e.map];
|
|
135
|
+
}
|
|
57
136
|
}
|
|
58
137
|
}
|
|
138
|
+
// A function's value at a key (the GPU search tabulates functions with it).
|
|
139
|
+
export const fnValue = (fn, key) => naturalIn(fnOf(fn)(key), `a function at ${key}`);
|
|
140
|
+
// A function's JavaScript, compiled once.
|
|
141
|
+
const compiledFns = new WeakMap();
|
|
142
|
+
const fnOf = (fn) => {
|
|
143
|
+
let f = compiledFns.get(fn);
|
|
144
|
+
if (!f) {
|
|
145
|
+
f = compileFunction(['k'], `return (${fn.js})(k)`);
|
|
146
|
+
compiledFns.set(fn, f);
|
|
147
|
+
}
|
|
148
|
+
return f;
|
|
149
|
+
};
|
|
59
150
|
export function evalC(c, env, st, m) {
|
|
60
151
|
switch (c.op) {
|
|
61
152
|
case '<': return evalE(c.a, env, st, m) < evalE(c.b, env, st, m);
|
|
@@ -65,7 +156,33 @@ export function evalC(c, env, st, m) {
|
|
|
65
156
|
case 'or': return c.xs.some(y => evalC(y, env, st, m));
|
|
66
157
|
case 'not': return !evalC(c.x, env, st, m);
|
|
67
158
|
case 'flag': return env[`?${c.name}`] === 1;
|
|
159
|
+
case 'all': return (plainKey(c.c, c.key) ? keysAt(st, mapsIn(c.c)) : breakKeys(breaksOf(c.c, c.key), env, st, m)).every(key => evalC(c.c, { ...env, [c.key]: key }, st, m));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// A write at every key: its value at each key the maps it reads hold a cell at, and its new default past them.
|
|
163
|
+
// With the key in key terms, every key up to the last breakpoint is computed (the stretches between breakpoints hold the
|
|
164
|
+
// same value, which need not be the default), and past it the value is the new default.
|
|
165
|
+
function writeEvery(w, env, st, m) {
|
|
166
|
+
if (!plainKey(w.value, w.each)) {
|
|
167
|
+
const top = breakKeys(breaksOf(w.value, w.each), env, st, m).at(-1);
|
|
168
|
+
if (top > 1_000_000)
|
|
169
|
+
throw new RangeError(`a write at every key reads its key in arithmetic past a million keys (${top})`);
|
|
170
|
+
const fallback = evalE(w.value, { ...env, [w.each]: top }, st, m), cells = new Map();
|
|
171
|
+
for (let key = 0; key < top; key++) {
|
|
172
|
+
const v = evalE(w.value, { ...env, [w.each]: key }, st, m);
|
|
173
|
+
if (v !== fallback)
|
|
174
|
+
cells.set(key, v);
|
|
175
|
+
}
|
|
176
|
+
return { cells, default: fallback };
|
|
177
|
+
}
|
|
178
|
+
const keys = keysAt(st, mapsIn(w.value)), past = keys.at(-1);
|
|
179
|
+
const fallback = evalE(w.value, { ...env, [w.each]: past }, st, m), cells = new Map();
|
|
180
|
+
for (const key of keys.slice(0, -1)) {
|
|
181
|
+
const v = evalE(w.value, { ...env, [w.each]: key }, st, m);
|
|
182
|
+
if (v !== fallback)
|
|
183
|
+
cells.set(key, v);
|
|
68
184
|
}
|
|
185
|
+
return { cells, default: fallback };
|
|
69
186
|
}
|
|
70
187
|
// The machine a runtime steps (with a history, kernel.ts's plain machine), its queries, and the inputs a request carries.
|
|
71
188
|
function runnable(m) {
|
|
@@ -77,6 +194,26 @@ function runnable(m) {
|
|
|
77
194
|
return { h, plain, queries, carried: m.inputs.filter(i => !queries.includes(i)) };
|
|
78
195
|
}
|
|
79
196
|
const empty = { scalars: {}, maps: {} };
|
|
197
|
+
// The state as a step reads it: with the maps the request carries (cells past them read 0), each checked natural.
|
|
198
|
+
export function carriedView(m, st, input, rename = map => map) {
|
|
199
|
+
if (!m.inputMaps?.length)
|
|
200
|
+
return st;
|
|
201
|
+
const maps = { ...st.maps }, defaults = { ...st.defaults };
|
|
202
|
+
for (const map of m.inputMaps) {
|
|
203
|
+
const cells = input[map];
|
|
204
|
+
if (!(cells instanceof Map))
|
|
205
|
+
throw new RangeError(`input ${map} must be a Map of naturals`);
|
|
206
|
+
for (const [key, v] of cells) {
|
|
207
|
+
naturalIn(key, `a key of ${map}`);
|
|
208
|
+
naturalIn(v, `a cell of ${map}`);
|
|
209
|
+
}
|
|
210
|
+
maps[rename(map)] = cells;
|
|
211
|
+
defaults[rename(map)] = 0;
|
|
212
|
+
}
|
|
213
|
+
return { ...st, maps, defaults };
|
|
214
|
+
}
|
|
215
|
+
// A request's numbers (its inputs), without the maps it carries.
|
|
216
|
+
export const numbersOf = (input) => Object.fromEntries(Object.entries(input).filter((e) => typeof e[1] === 'number'));
|
|
80
217
|
// The interpreter: persistent (a step leaves the state it was given as it was), so searches can branch from any state.
|
|
81
218
|
export function interpret(m) {
|
|
82
219
|
const { h, plain, carried } = runnable(m);
|
|
@@ -89,6 +226,7 @@ export function interpret(m) {
|
|
|
89
226
|
env[i] = naturalIn(input[i], `input ${i}`);
|
|
90
227
|
return env;
|
|
91
228
|
};
|
|
229
|
+
const viewed = (st, input) => carriedView(m, st, input);
|
|
92
230
|
// The records a request's queries read: with pruning, those not yet spent at its order.
|
|
93
231
|
const live = (env, st) => {
|
|
94
232
|
const records = st.history;
|
|
@@ -134,8 +272,8 @@ export function interpret(m) {
|
|
|
134
272
|
ordered(env, st);
|
|
135
273
|
return queriesOf(env, live(env, st));
|
|
136
274
|
},
|
|
137
|
-
step: (params,
|
|
138
|
-
const env = args(params, input);
|
|
275
|
+
step: (params, given, input) => {
|
|
276
|
+
const env = args(params, input), st = viewed(given, input);
|
|
139
277
|
let records = [];
|
|
140
278
|
if (h) {
|
|
141
279
|
ordered(env, st);
|
|
@@ -147,12 +285,19 @@ export function interpret(m) {
|
|
|
147
285
|
const scalars = { ...st.scalars };
|
|
148
286
|
for (const [name, e] of Object.entries(plain.step.state ?? {}))
|
|
149
287
|
scalars[name] = evalE(e, env, st, plain);
|
|
150
|
-
const writes = Object.entries(plain.step.maps ?? {}).map(([name,
|
|
151
|
-
const maps = Object.fromEntries(Object.entries(
|
|
152
|
-
|
|
153
|
-
|
|
288
|
+
const writes = Object.entries(plain.step.maps ?? {}).map(([name, w]) => [name, 'each' in w ? writeEvery(w, env, st, plain) : { key: evalE(w.key, env, st, plain), value: evalE(w.value, env, st, plain) }]);
|
|
289
|
+
const maps = Object.fromEntries(Object.entries(given.maps).map(([name, cells]) => [name, new Map(cells)]));
|
|
290
|
+
const defaults = { ...given.defaults };
|
|
291
|
+
for (const [name, w] of writes)
|
|
292
|
+
if ('cells' in w) {
|
|
293
|
+
maps[name] = w.cells;
|
|
294
|
+
defaults[name] = w.default;
|
|
295
|
+
}
|
|
296
|
+
else
|
|
297
|
+
maps[name].set(w.key, w.value);
|
|
298
|
+
const withDefaults = Object.keys(defaults).length ? { defaults } : {};
|
|
154
299
|
if (!h)
|
|
155
|
-
return { scalars, maps };
|
|
300
|
+
return { scalars, maps, ...withDefaults };
|
|
156
301
|
const kept = evalC(h.keep, env, st, plain);
|
|
157
302
|
return { scalars, maps, history: kept ? [...records, Object.fromEntries(h.record.map(f => [f, env[f]]))] : records, ...(h.prune ? { last: env[h.prune.order] } : {}) };
|
|
158
303
|
},
|
|
@@ -164,10 +309,12 @@ export function interpret(m) {
|
|
|
164
309
|
export function compileJs(m) {
|
|
165
310
|
const { h, plain, queries, carried } = runnable(m);
|
|
166
311
|
const id = (name) => JSON.stringify(name);
|
|
167
|
-
const flags = Object.keys(plain.step.flags ?? {});
|
|
312
|
+
const flags = Object.keys(plain.step.flags ?? {}), carriedMaps = m.inputMaps ?? [], fns = Object.keys(m.fns ?? {});
|
|
313
|
+
// A key bound by a quantified flag or a write at every key is the JS parameter k.
|
|
314
|
+
let bound;
|
|
168
315
|
const ex = (e) => {
|
|
169
316
|
switch (e.op) {
|
|
170
|
-
case 'var': return e.name.startsWith('r.') ? `r[${id(e.name.slice(2))}]` : plain.params.includes(e.name) || plain.inputs.includes(e.name) ? `env[${id(e.name)}]` : `s[${id(e.name)}]`;
|
|
317
|
+
case 'var': return e.name === bound ? 'k' : e.name.startsWith('r.') ? `r[${id(e.name.slice(2))}]` : plain.params.includes(e.name) || plain.inputs.includes(e.name) ? `env[${id(e.name)}]` : `s[${id(e.name)}]`;
|
|
171
318
|
case 'const': return String(e.value);
|
|
172
319
|
case 'add': return `$x(${ex(e.a)} + ${ex(e.b)})`;
|
|
173
320
|
case 'sub': return `Math.max(0, ${ex(e.a)} - ${ex(e.b)})`;
|
|
@@ -176,7 +323,7 @@ export function compileJs(m) {
|
|
|
176
323
|
case 'min': return `Math.min(${ex(e.a)}, ${ex(e.b)})`;
|
|
177
324
|
case 'max': return `Math.max(${ex(e.a)}, ${ex(e.b)})`;
|
|
178
325
|
case 'if': return `(${cx(e.c)} ? ${ex(e.then)} : ${ex(e.else)})`;
|
|
179
|
-
case 'get': return `(maps[${id(e.map)}].get(${ex(e.key)}) ?? ${plain.maps[e.map]})`;
|
|
326
|
+
case 'get': return fns.includes(e.map) ? `$n(fn${fns.indexOf(e.map)}(${ex(e.key)}), ${id(`${e.map}()`)})` : `(maps[${id(e.map)}].get(${ex(e.key)}) ?? ${carriedMaps.includes(e.map) ? '0' : `dft[${id(e.map)}] ?? ${plain.maps[e.map]}`})`;
|
|
180
327
|
}
|
|
181
328
|
};
|
|
182
329
|
const cx = (c) => {
|
|
@@ -188,11 +335,27 @@ export function compileJs(m) {
|
|
|
188
335
|
case 'or': return `(${c.xs.map(cx).join(' || ')})`;
|
|
189
336
|
case 'not': return `(!${cx(c.x)})`;
|
|
190
337
|
case 'flag': return `flag${flags.indexOf(c.name)}`;
|
|
338
|
+
case 'all': {
|
|
339
|
+
const keys = keysJs(c.c, c.key), outer = bound;
|
|
340
|
+
bound = c.key;
|
|
341
|
+
const body = cx(c.c);
|
|
342
|
+
bound = outer;
|
|
343
|
+
return `${keys}.every(k => ${body})`;
|
|
344
|
+
}
|
|
191
345
|
}
|
|
192
346
|
};
|
|
347
|
+
// The keys a quantifier or a write at every key is decided on (the interpreter's keysAt, or its breakKeys).
|
|
348
|
+
const keysJs = (t, key) => {
|
|
349
|
+
if (plainKey(t, key))
|
|
350
|
+
return `$keys(maps, ${JSON.stringify(mapsIn(t).filter(mp => !fns.includes(mp)))})`;
|
|
351
|
+
const b = breaksOf(t, key), off = (o) => (o ? ex(o) : '0');
|
|
352
|
+
return `$breaks(maps, [${b.reads.map(r => `[${id(r.map)}, ${off(r.off)}, ${r.minus}]`).join(', ')}], [${b.cmps.map(c => `[${ex(c.value)}, ${off(c.off)}, ${c.minus}]`).join(', ')}])`;
|
|
353
|
+
};
|
|
354
|
+
const every = (w) => { const keys = keysJs(w.value, w.each), dense = !plainKey(w.value, w.each), outer = bound; bound = w.each; const body = ex(w.value); bound = outer; return `${dense ? '$dense' : '$every'}(${keys}, k => ${body})`; };
|
|
193
355
|
const writes = Object.entries(plain.step.maps ?? {});
|
|
194
356
|
const env = (input) => `{${[...m.params.map(p => `${id(p)}: $n(params[${id(p)}], ${id(`param ${p}`)})`), ...(input ? carried.map(i => `${id(i)}: $n(input[${id(i)}], ${id(`input ${i}`)})`) : [])].join(', ')}}`;
|
|
195
|
-
|
|
357
|
+
// Reads go through `maps` (with the maps the request carries); writes go to the state's own.
|
|
358
|
+
const lines = [`const env = ${env(true)}, s = state.scalars, dft = {...state.defaults}, maps = ${carriedMaps.length ? `{...state.maps, ${carriedMaps.map(mp => `${id(mp)}: $m(input[${id(mp)}], ${id(`input ${mp}`)})`).join(', ')}}` : 'state.maps'};`];
|
|
196
359
|
if (h) {
|
|
197
360
|
lines.push('const records = state.history;', 'let head = state.head;');
|
|
198
361
|
if (h.prune) {
|
|
@@ -201,33 +364,39 @@ export function compileJs(m) {
|
|
|
201
364
|
}
|
|
202
365
|
lines.push(`let ${queries.map((_, i) => `q${i} = 0`).join(', ')};`, `for (let i = head; i < records.length; i++) { const r = records[i]; ${queries.map((q, i) => `q${i} = $x(q${i} + ${ex(h.queries[q])});`).join(' ')} }`, ...queries.map((q, i) => `env[${id(q)}] = q${i};`));
|
|
203
366
|
}
|
|
204
|
-
lines.push(...flags.map((name, i) => `const flag${i} = ${cx(plain.step.flags[name])};`), `const scalars = {...s, ${Object.entries(plain.step.state ?? {}).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}};`, ...writes.map(([,
|
|
367
|
+
lines.push(...flags.map((name, i) => `const flag${i} = ${cx(plain.step.flags[name])};`), `const scalars = {...s, ${Object.entries(plain.step.state ?? {}).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}};`, ...writes.map(([, w], i) => ('each' in w ? `const write${i} = ${every(w)};` : `const key${i} = ${ex(w.key)}, value${i} = ${ex(w.value)};`)));
|
|
205
368
|
if (h)
|
|
206
369
|
lines.push(`const keep = ${cx(h.keep)};`);
|
|
207
|
-
lines.push(...writes.map(([name], i) => `maps[${id(name)}].set(key${i}, value${i});`));
|
|
370
|
+
lines.push(...writes.map(([name, w], i) => ('each' in w ? `state.maps[${id(name)}] = write${i}.cells; dft[${id(name)}] = write${i}.d;` : `state.maps[${id(name)}].set(key${i}, value${i});`)));
|
|
208
371
|
if (h)
|
|
209
372
|
lines.push(`if (keep) records.push({${h.record.map(f => `${id(f)}: env[${id(f)}]`).join(', ')}});`,
|
|
210
373
|
// Drop the spent prefix now and then, not every step: amortized, each record is moved a bounded number of times.
|
|
211
|
-
'if (head > 1024 && 2 * head > records.length) { records.splice(0, head); head = 0; }', `return {scalars, maps, history: records, head${h.prune ? ', last: order' : ''}};`);
|
|
374
|
+
'if (head > 1024 && 2 * head > records.length) { records.splice(0, head); head = 0; }', `return {scalars, maps: state.maps, history: records, head${h.prune ? ', last: order' : ''}};`);
|
|
212
375
|
else
|
|
213
|
-
lines.push('return {scalars, maps};');
|
|
376
|
+
lines.push('return Object.keys(dft).length ? {scalars, maps: state.maps, defaults: dft} : {scalars, maps: state.maps};');
|
|
214
377
|
return `// Generated from the machine ${m.name}. step consumes its state: keep the one it returns (fork to branch).
|
|
215
378
|
function $n(v, what) { if (typeof v !== 'number' || !Number.isSafeInteger(v) || v < 0) throw new RangeError(what + ' must be a natural below 2^53, not ' + v); return v; }
|
|
216
379
|
function $x(v) { if (v > 9007199254740991) throw new RangeError('a value passed 2^53 - 1, past which the proofs over the naturals no longer describe the run'); return v; }
|
|
380
|
+
function $m(v, what) { if (!(v instanceof Map)) throw new RangeError(what + ' must be a Map of naturals'); for (const [key, x] of v) { $n(key, 'a key of ' + what); $n(x, 'a cell of ' + what); } return v; }
|
|
381
|
+
function $keys(maps, names) { const keys = new Set(); let past = 0; for (const c of Object.values(maps)) for (const key of c.keys()) past = Math.max(past, key + 1); for (const n of names) for (const key of maps[n].keys()) keys.add(key); return [...keys, past]; }
|
|
382
|
+
${fns.map((name, i) => `const fn${i} = (${m.fns[name].js});\n`).join('')}function $breaks(maps, reads, cmps) { const p = new Set([0]); for (const [n, d, minus] of reads) { for (const s of maps[n].keys()) { if (minus) p.add(s + d); else if (s >= d) p.add(s - d); } if (minus) p.add(d); } for (const [v, d, minus] of cmps) { if (minus) { p.add(v + d); p.add(d); } else if (v >= d) p.add(v - d); } const keys = new Set(); for (const q of p) for (let i = -1; i <= 2; i++) if (q + i >= 0) keys.add(q + i); const sorted = [...keys].sort((a, b) => a - b); sorted.push(sorted[sorted.length - 1] + 1); return sorted; }
|
|
383
|
+
function $dense(keys, f) { const top = keys[keys.length - 1]; if (top > 1000000) throw new RangeError('a write at every key reads its key in arithmetic past a million keys (' + top + ')'); const d = f(top), cells = new Map(); for (let k = 0; k < top; k++) { const v = f(k); if (v !== d) cells.set(k, v); } return {cells, d}; }
|
|
384
|
+
function $every(keys, f) { const past = keys[keys.length - 1], d = f(past), cells = new Map(); for (let i = 0; i < keys.length - 1; i++) { const v = f(keys[i]); if (v !== d) cells.set(keys[i], v); } return {cells, d}; }
|
|
217
385
|
export function init(params) {
|
|
218
386
|
const env = ${env(false)}, s = {};
|
|
219
387
|
return {scalars: {${Object.entries(plain.state).map(([name, e]) => `${id(name)}: ${ex(e)}`).join(', ')}}, maps: {${Object.keys(plain.maps ?? {}).map(name => `${id(name)}: new Map()`).join(', ')}}${h ? ', history: [], head: 0' : ''}};
|
|
220
388
|
}
|
|
221
389
|
export function fork(state) {
|
|
222
|
-
return {scalars: {...state.scalars}, maps: Object.fromEntries(Object.entries(state.maps).map(([name, cells]) => [name, new Map(cells)]))${h ? ', history: state.history.slice(state.head), head: 0, last: state.last' : ''}};
|
|
390
|
+
return {scalars: {...state.scalars}, maps: Object.fromEntries(Object.entries(state.maps).map(([name, cells]) => [name, new Map(cells)])), ...(state.defaults ? {defaults: {...state.defaults}} : {})${h ? ', history: state.history.slice(state.head), head: 0, last: state.last' : ''}};
|
|
223
391
|
}
|
|
224
392
|
export function step(params, state, input) {
|
|
225
393
|
${lines.join('\n ')}
|
|
226
394
|
}
|
|
227
395
|
`;
|
|
228
396
|
}
|
|
229
|
-
// Generated code as a function. Every caller passes source this library generated from its own terms,
|
|
230
|
-
//
|
|
397
|
+
// Generated code as a function. Every caller passes source this library generated from its own terms, or a function's
|
|
398
|
+
// `js` the rule's author wrote (code, as the rule itself is), never data; the lint's implied-eval rule is waived here
|
|
399
|
+
// and nowhere else.
|
|
231
400
|
export function compileFunction(params, body) {
|
|
232
401
|
// oxlint-disable-next-line typescript/no-implied-eval
|
|
233
402
|
return new Function(...params, body);
|
|
@@ -279,6 +448,7 @@ function symE(e, s) {
|
|
|
279
448
|
// The cases in which c has the given truth value.
|
|
280
449
|
function symC(c, s, truth) {
|
|
281
450
|
switch (c.op) {
|
|
451
|
+
case 'all': throw new Error('the symbolic prover takes no quantified flag (the kernel does)');
|
|
282
452
|
case '<':
|
|
283
453
|
case '<=':
|
|
284
454
|
case '=':
|
|
@@ -318,7 +488,7 @@ function feasible(cases) {
|
|
|
318
488
|
function readsOf(c) {
|
|
319
489
|
const inE = (e) => e.op === 'get' ? [{ map: e.map, key: e.key }, ...inE(e.key)]
|
|
320
490
|
: e.op === 'if' ? [...readsOfC(e.c), ...inE(e.then), ...inE(e.else)] : 'a' in e ? [...inE(e.a), ...('b' in e ? inE(e.b) : [])] : [];
|
|
321
|
-
const readsOfC = (y) => y.op === 'flag' ? [] : y.op === 'not' ? readsOfC(y.x) : y.op === 'and' || y.op === 'or' ? y.xs.flatMap(readsOfC) : [...inE(y.a), ...inE(y.b)];
|
|
491
|
+
const readsOfC = (y) => y.op === 'flag' || y.op === 'all' ? [] : y.op === 'not' ? readsOfC(y.x) : y.op === 'and' || y.op === 'or' ? y.xs.flatMap(readsOfC) : [...inE(y.a), ...inE(y.b)];
|
|
322
492
|
return readsOfC(c);
|
|
323
493
|
}
|
|
324
494
|
const keyLin = (e, names) => symE(e, { names, reads: noReads })[0].value;
|
|
@@ -389,7 +559,7 @@ export function prove(m, goal, invariants = [goal]) {
|
|
|
389
559
|
const stepReads = [
|
|
390
560
|
...Object.values(m.step.flags ?? {}).flatMap(readsOf),
|
|
391
561
|
...Object.values(m.step.state ?? {}).flatMap(e => readsOf({ op: '=', a: e, b: e })),
|
|
392
|
-
...Object.entries(m.step.maps ?? {}).flatMap(([map, w]) => [{ map, key: w.key }, ...readsOf({ op: '=', a: w.value, b: w.value })]),
|
|
562
|
+
...Object.entries(m.step.maps ?? {}).flatMap(([map, w]) => [...('each' in w ? [] : [{ map, key: w.key }]), ...readsOf({ op: '=', a: w.value, b: w.value })]),
|
|
393
563
|
];
|
|
394
564
|
const pairs = new Map();
|
|
395
565
|
for (const r of stepReads) {
|
|
@@ -424,6 +594,8 @@ export function prove(m, goal, invariants = [goal]) {
|
|
|
424
594
|
cases = feasible(cross(cases, acc => {
|
|
425
595
|
if (write === undefined)
|
|
426
596
|
return [{ path: [], value: { ...acc, [`${f.cell}'`]: v(f.cell) } }];
|
|
597
|
+
if ('each' in write)
|
|
598
|
+
throw new Error('the symbolic prover takes no write at every key (the kernel does)');
|
|
427
599
|
return cross(symE(write.key, preSymF), key => {
|
|
428
600
|
const decided = preSymF.decided?.[`${show(key)}|${show(f.key)}`];
|
|
429
601
|
const written = () => symE(write.value, preSymF).map(val => ({ path: [...val.path, ...(decided ? [] : eq(key, f.key))], value: { ...acc, [`${f.cell}'`]: val.value } }));
|
package/dist/kernel/kernel.d.ts
CHANGED
|
@@ -25,14 +25,17 @@ export declare function facts(c: C, path: Path, positive?: boolean): Lin[];
|
|
|
25
25
|
export declare function leafConstraints(goal: Goal, path: Path): Constraint[];
|
|
26
26
|
export declare function withProducts(rows: Constraint[], pairs: [number, number][]): Constraint[];
|
|
27
27
|
export declare function check(goal: Goal, tree: Tree, path?: Path): boolean;
|
|
28
|
-
type Subst = {
|
|
28
|
+
export type Subst = {
|
|
29
29
|
vars: Record<string, E>;
|
|
30
30
|
maps: Record<string, (key: E) => E>;
|
|
31
31
|
flags: Record<string, C>;
|
|
32
32
|
};
|
|
33
|
+
export declare function substE(e: E, s: Subst): E;
|
|
33
34
|
export declare function substC(c: C, s: Subst): C;
|
|
34
35
|
export type Instances = Record<string, E[]>;
|
|
35
36
|
export declare function scope(m: Machine, laws?: Law[]): void;
|
|
37
|
+
export declare const keyTerm: (e: E, key: string) => boolean;
|
|
38
|
+
export declare const plainKey: (t: E | C, key: string) => boolean;
|
|
36
39
|
export declare function historyMachine(m: Machine): {
|
|
37
40
|
machine: Machine;
|
|
38
41
|
goals: Goal[];
|
|
@@ -59,11 +62,35 @@ export declare function progressObligations(m: Machine, laws: Law[], instances:
|
|
|
59
62
|
then: C;
|
|
60
63
|
request: Record<string, E>;
|
|
61
64
|
}): Goal[];
|
|
65
|
+
export type Helpful = {
|
|
66
|
+
when?: C;
|
|
67
|
+
request: Record<string, E>;
|
|
68
|
+
};
|
|
69
|
+
export type Liveness = {
|
|
70
|
+
name: string;
|
|
71
|
+
from: C;
|
|
72
|
+
to: C;
|
|
73
|
+
rank: E | E[];
|
|
74
|
+
request?: Record<string, E>;
|
|
75
|
+
helpful?: Helpful[];
|
|
76
|
+
};
|
|
77
|
+
export declare const trueC: C, falseC: C;
|
|
78
|
+
export declare const livenessRanks: (l: Liveness) => E[];
|
|
79
|
+
export declare const livenessHelpful: (l: Liveness) => {
|
|
80
|
+
when: C;
|
|
81
|
+
request: Record<string, E>;
|
|
82
|
+
}[];
|
|
83
|
+
export declare function livenessParts(m: Machine, l: Liveness): {
|
|
84
|
+
stay: StepLaw;
|
|
85
|
+
cover: StepLaw;
|
|
86
|
+
helps: StepLaw[];
|
|
87
|
+
};
|
|
62
88
|
export type StepLaw = {
|
|
63
89
|
name: string;
|
|
64
90
|
when?: C | C[];
|
|
65
91
|
then: C;
|
|
66
92
|
forall?: string;
|
|
93
|
+
keys?: string[];
|
|
67
94
|
steps?: number;
|
|
68
95
|
};
|
|
69
96
|
export declare function stepLawObligations(machine: Machine, laws: Law[], instances: Instances, sl: StepLaw): Goal[];
|
|
@@ -73,6 +100,7 @@ export declare function checkStepLaws(m: Machine, laws: Law[], stepLaws: StepLaw
|
|
|
73
100
|
trees: Record<string, Tree>;
|
|
74
101
|
};
|
|
75
102
|
instances: Instances;
|
|
103
|
+
perStep?: Record<string, Instances>;
|
|
76
104
|
trees: Record<string, Tree>;
|
|
77
105
|
}): boolean;
|
|
78
106
|
export type Fold = {
|
|
@@ -85,4 +113,3 @@ export type Fold = {
|
|
|
85
113
|
assume?: C;
|
|
86
114
|
};
|
|
87
115
|
export declare function foldObligations(f: Fold, laws: Law[]): Goal[];
|
|
88
|
-
export {};
|