gdp-wired 0.10.0 → 0.11.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 +18 -6
- package/dist/core/dsl.d.ts +106 -8
- package/dist/core/dsl.js +310 -31
- package/dist/core/gdp.js +2 -2
- package/dist/core/machine.d.ts +3 -0
- package/dist/core/machine.js +41 -3
- package/dist/core/solver.d.ts +1 -0
- package/dist/core/solver.js +116 -5
- package/dist/kernel/kernel.d.ts +9 -1
- package/dist/kernel/kernel.js +171 -13
- package/dist/kernel/prove.d.ts +21 -0
- package/dist/kernel/prove.js +128 -22
- package/dist/lean-kernel/emit.js +8 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -69,8 +69,9 @@ All of these are proven automatically, and each proof is also a Lean theorem:
|
|
|
69
69
|
(`examples/datatypes.ts`).
|
|
70
70
|
- **Lists a request carries, and trees**: an invoice's lines, with folds over them (the total, the taxable part) and
|
|
71
71
|
lemmas about the folds proven by induction over the lines (`examples/invoices.ts`); a fold over a tree as a value (a
|
|
72
|
-
spend roll-up
|
|
73
|
-
(`examples/folds.ts`)
|
|
72
|
+
spend roll-up, sign-offs over a document's sections, an org chart where a manager has any number of reports), its laws
|
|
73
|
+
proven by induction over the tree (`examples/folds.ts`); a search tree, and a sheet of rows of cells, kept in a rule's
|
|
74
|
+
state as tables of nodes (`examples/trees.ts`).
|
|
74
75
|
- **Pure functions and two-run laws**: laws about what a function returns, and laws comparing two calls, like "the
|
|
75
76
|
secret never changes the answer". `examples/jev-fabric/` ports a Bend project's policy core and proves all 59 laws its
|
|
76
77
|
Bend proofs state.
|
|
@@ -228,13 +229,24 @@ const soon = retry.law('the next try is at most 64 after the last request', ({ne
|
|
|
228
229
|
carried map of its running values whose equations every request is assumed to meet (the encoding computes them from the
|
|
229
230
|
array, and a request that doesn't meet them isn't allowed), read as `Lines.fold.total`; `Lines.every`, `some`, `has`,
|
|
230
231
|
`increasing(by)`, `at(i)`. A lemma about the folds (`({taxed, total}) => taxed.atMost(total)`) is proven by induction
|
|
231
|
-
over the items when the rule is built, then assumed at every prefix
|
|
232
|
+
over the items when the rule is built, then assumed at every prefix. A fold's start and step, and a lemma, may read the
|
|
233
|
+
params (`{from: ({fee}) => fee, step: (acc, line, {ceil}) => acc.plus(min(line.amount, ceil))}`); a request is then
|
|
234
|
+
encoded with them, `rule.encodeInput(values, params)` (`examples/invoices.ts`).
|
|
232
235
|
- **Folds over trees** (`fold(name).params(...).inputs(...).leaf(env => ({...})).node((env, left, right) => ({...}))`,
|
|
233
236
|
`proveFold`): a tree as a value (`{leaf: inputs}` or `{node: inputs, left, right}`), each leaf's values from its inputs
|
|
234
237
|
and each node's from its own and its children's; a law about the values is proven by induction over the tree (the
|
|
235
238
|
kernel's leaf and node goals, Lean's `fold_sound`), `f.run(tree, params)` evaluates the terms proven and `f.crossCheck`
|
|
236
|
-
holds the builders to them
|
|
237
|
-
|
|
239
|
+
holds the builders to them. Nodes with any number of children: `.children(from, (acc, kid) => ..., lemmas)` folds a
|
|
240
|
+
node's children into `kids`, then `.node((env, kids) => ...)`; it is the kernel's binary fold underneath (each child
|
|
241
|
+
list a chain of glue nodes), so Lean's `fold_sound` checks it unchanged, and `f.run` takes `{node, children: [...]}`
|
|
242
|
+
(`examples/folds.ts`).
|
|
243
|
+
- **Trees in a rule's state, and lists of lists** (`.nodes('Tree', record({key: nat}), ['left', 'right'], folds)`): a
|
|
244
|
+
table of nodes that only grows, node 0 the empty tree, `Tree.make(value, {left, right}, when)` making node size + 1 from
|
|
245
|
+
nodes already there; each fold (`{lo: {empty: 0, node: (x, kids) => when(kids.left.empty, x.key, kids.left.fold.lo)}}`)
|
|
246
|
+
kept with its node when it is made, and may read a table declared before (a row's total over its cells). A law about
|
|
247
|
+
a subtree is then a law about one node (`Tree.node(n).fold.lo`), proven like any law about every key; the table's own
|
|
248
|
+
laws (children earlier, folds coherent, nothing past the end) are proven beside it (`examples/trees.ts`: a search tree,
|
|
249
|
+
a sheet of rows of cells). Claims that need a lemma about the tree's shape nobody stated stay Lean's.
|
|
238
250
|
- **State of any kind**: `.state({open: bool.from(true), status: oneOf('new', 'done').from('new'), owner: str().from(''), note: maybe(nat).from(null)})`;
|
|
239
251
|
a step writes each kind's own values, `when(c, a, b)` chooses between any two values of a kind, and `rule.decode(state)`
|
|
240
252
|
reads them back as runtime values.
|
|
@@ -329,7 +341,7 @@ Lean, compiled natively, wins where the state stays small. Details in [`docs/com
|
|
|
329
341
|
| `core/` | Machines, the interpreter and JS compiler, the builders, composition (`compose.ts`), the linear solver and its checker, Houdini invariants, models, the first Lean export |
|
|
330
342
|
| `kernel/` | The trusted kernel and the untrusted prover, with soundness fuzz (`kernel.test.ts`, `history.test.ts`) |
|
|
331
343
|
| `lean-kernel/` | The kernel's Lean twin, its soundness proofs, and the exported trees |
|
|
332
|
-
| `examples/` | The builders at work: `domains/` (187 rules across 21 domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `gdp.ts` (gdp-ts's example, its checks proven), `datatypes.ts` (records and variants), `lemmas.ts` (recurrences and their lemmas), `invoices.ts` (lists a request carries), `folds.ts` (folds over trees), `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
|
|
344
|
+
| `examples/` | The builders at work: `domains/` (187 rules across 21 domains, each with laws and broken variants), `harness.ts` (the triple check), `readme.ts`, `budget.ts`, `gdp.ts` (gdp-ts's example, its checks proven), `datatypes.ts` (records and variants), `lemmas.ts` (recurrences and their lemmas), `invoices.ts` (lists a request carries), `folds.ts` (folds over trees), `trees.ts` (trees and lists of lists in the state), `composition.ts`, `sets.ts`, `quantifiers.ts`, `functions.ts`, `lists.ts`, `sizes.ts`, `liveness.ts`, `steps.ts` (laws over up to 16 steps), and `jev-fabric/` (a Bend project's 59 proven laws, ported) |
|
|
333
345
|
| `docs/` | The comparison with Bend and Lean, and the full scope |
|
|
334
346
|
| `compare/` | The same three rules in gdp-wired, Bend and plain Lean, checked and measured |
|
|
335
347
|
| `research/` | Everything around the library ([`research/README.md`](research/README.md)): the GPU searches (`search/`, `hw/`, `modal-gpu/`), invariant and rank synthesis (`synth/`), relational laws and guard synthesis (`next/`), bit-vector laws (`sat/`), the enumeration and benchmarks (`enum/`, `gpu/`, `perf/`), the first Lean checker (`lean/`) and the first prototype, the audit path (`audit/`) |
|
package/dist/core/dsl.d.ts
CHANGED
|
@@ -310,11 +310,11 @@ export type ItemsOp = {
|
|
|
310
310
|
readonly parts: Record<string, ListOp>;
|
|
311
311
|
readonly len: TermLike;
|
|
312
312
|
};
|
|
313
|
-
export type ListFold<V> = {
|
|
314
|
-
from: number;
|
|
315
|
-
step: (acc: Term, item: V) => TermLike;
|
|
313
|
+
export type ListFold<V, P = any> = {
|
|
314
|
+
from: number | ((p: P) => TermLike);
|
|
315
|
+
step: (acc: Term, item: V, p: P) => TermLike;
|
|
316
316
|
};
|
|
317
|
-
export type ListLemma = (acc: Record<string, Term>, i: Term) => Cond;
|
|
317
|
+
export type ListLemma<P = any> = (acc: Record<string, Term>, i: Term, p: P) => Cond;
|
|
318
318
|
export declare class CarriedList<V, Fd extends string = never> {
|
|
319
319
|
readonly name: string;
|
|
320
320
|
readonly kind: Kind<V>;
|
|
@@ -322,8 +322,10 @@ export declare class CarriedList<V, Fd extends string = never> {
|
|
|
322
322
|
readonly folds: Record<Fd, ListFold<V>>;
|
|
323
323
|
private readonly map;
|
|
324
324
|
readonly lemmas: Record<string, ListLemma>;
|
|
325
|
+
private readonly params?;
|
|
326
|
+
private readonly paramKinds;
|
|
325
327
|
readonly fold: Record<Fd, Term>;
|
|
326
|
-
constructor(name: string, kind: Kind<V>, len: Term, folds: Record<Fd, ListFold<V>>, map: (part: string) => MapRef, lemmas?: Record<string, ListLemma>);
|
|
328
|
+
constructor(name: string, kind: Kind<V>, len: Term, folds: Record<Fd, ListFold<V>>, map: (part: string) => MapRef, lemmas?: Record<string, ListLemma>, params?: Record<string, unknown> | undefined, paramKinds?: [string, Kind<unknown>][]);
|
|
327
329
|
at(i: TermLike): V;
|
|
328
330
|
after(fold: Fd, i: TermLike): Term;
|
|
329
331
|
every(f: (item: V, i: Term) => Cond): Cond;
|
|
@@ -331,6 +333,8 @@ export declare class CarriedList<V, Fd extends string = never> {
|
|
|
331
333
|
has(x: V): Cond;
|
|
332
334
|
increasing(by: (item: V) => Term): Cond;
|
|
333
335
|
equations(): Cond[];
|
|
336
|
+
private get readsParams();
|
|
337
|
+
private p;
|
|
334
338
|
private running;
|
|
335
339
|
prove(): Verdict;
|
|
336
340
|
get maps(): string[];
|
|
@@ -358,6 +362,35 @@ export declare class ItemsRef<V> {
|
|
|
358
362
|
within(): Forall;
|
|
359
363
|
increasing(by: (x: V) => Term): Forall;
|
|
360
364
|
}
|
|
365
|
+
export type NodeRef<V, C extends string, Fd extends string> = {
|
|
366
|
+
readonly index: Term;
|
|
367
|
+
readonly empty: Cond;
|
|
368
|
+
readonly value: V;
|
|
369
|
+
readonly fold: Record<Fd, Term>;
|
|
370
|
+
readonly kids: Record<C, Term>;
|
|
371
|
+
};
|
|
372
|
+
export type NodeFold<V, C extends string, Fd extends string = string, T = Record<string, NodeTable<any, any, any>>> = {
|
|
373
|
+
empty: number;
|
|
374
|
+
node: (value: V, kids: Record<C, NodeRef<V, C, Fd>>, tables: T) => TermLike;
|
|
375
|
+
};
|
|
376
|
+
type TablesOf<M> = {
|
|
377
|
+
[k in keyof M as M[k] extends NodeTable<any, any, any> ? k : never]: M[k];
|
|
378
|
+
};
|
|
379
|
+
export declare class NodeTable<V, C extends string = string, Fd extends string = string> {
|
|
380
|
+
readonly name: string;
|
|
381
|
+
readonly kind: Kind<V>;
|
|
382
|
+
readonly children: C[];
|
|
383
|
+
readonly folds: Record<Fd, NodeFold<V, C, Fd>>;
|
|
384
|
+
readonly size: Term;
|
|
385
|
+
private readonly map;
|
|
386
|
+
private readonly tables;
|
|
387
|
+
constructor(name: string, kind: Kind<V>, children: C[], folds: Record<Fd, NodeFold<V, C, Fd>>, size: Term, map: (part: string) => MapRef, tables?: Record<string, NodeTable<any, any, any>>);
|
|
388
|
+
node(n: TermLike): NodeRef<V, C, Fd>;
|
|
389
|
+
at(n: TermLike): V;
|
|
390
|
+
fits(kids: Record<C, TermLike>): Cond;
|
|
391
|
+
make(value: Writes<V>, kids: Record<C, TermLike>, when?: Cond): Record<string, TermLike | PartsWrite>;
|
|
392
|
+
laws(): [string, Forall][];
|
|
393
|
+
}
|
|
361
394
|
export type Forall = {
|
|
362
395
|
readonly forall: (key: Term, ...more: Term[]) => Cond;
|
|
363
396
|
};
|
|
@@ -419,6 +452,7 @@ type Spec = {
|
|
|
419
452
|
lists?: string[];
|
|
420
453
|
cells?: [string, Kind<unknown>][];
|
|
421
454
|
items?: [string, Kind<unknown>][];
|
|
455
|
+
nodes?: [string, Kind<unknown>, string[], Record<string, NodeFold<unknown, string>>][];
|
|
422
456
|
kinded?: [string, Kind<unknown>][];
|
|
423
457
|
carried?: string[];
|
|
424
458
|
lists2?: [string, Kind<unknown>, Record<string, ListFold<unknown>>, Record<string, ListLemma>][];
|
|
@@ -467,6 +501,13 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
467
501
|
}, M & {
|
|
468
502
|
[k in keyof T]: ItemsRef<T[k] extends Kind<infer V> ? V : never>;
|
|
469
503
|
}, F, R, Su, Q>;
|
|
504
|
+
nodes<const N extends string, V, const C extends string, Fd extends string = never>(name: N, of: Kind<V>, children: readonly C[], folds?: {
|
|
505
|
+
[f in Fd]: NodeFold<V, C, Fd, TablesOf<M>>;
|
|
506
|
+
}): Rule<P, I, S & {
|
|
507
|
+
[k in N as `${k}_size`]: Term;
|
|
508
|
+
}, M & {
|
|
509
|
+
[k in N]: NodeTable<V, C, Fd>;
|
|
510
|
+
}, F, R, Su, Q>;
|
|
470
511
|
fns<T extends Record<string, FnDef>>(defs: T): Rule<P & {
|
|
471
512
|
[k in keyof T]: (...args: TermLike[]) => Term;
|
|
472
513
|
}, I, S, M, F, R, Su, Q>;
|
|
@@ -474,7 +515,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
474
515
|
carries<const N extends string>(...names: N[]): Rule<P, I & {
|
|
475
516
|
[k in N]: MapRef;
|
|
476
517
|
}, S, M, F, R, Su, Q>;
|
|
477
|
-
carriesList<const N extends string, V, Fd extends string = never>(name: N, of: Kind<V>, folds?: Record<Fd, ListFold<V>>, lemmas?: Record<string, (acc: Record<Fd, Term>, i: Term) => Cond>): Rule<P, I & {
|
|
518
|
+
carriesList<const N extends string, V, Fd extends string = never>(name: N, of: Kind<V>, folds?: Record<Fd, ListFold<V, P>>, lemmas?: Record<string, (acc: Record<Fd, Term>, i: Term, p: P) => Cond>): Rule<P, I & {
|
|
478
519
|
[k in N]: CarriedList<V, Fd>;
|
|
479
520
|
}, S, M, F, R, Su, Q>;
|
|
480
521
|
flags<T extends Record<string, Cond>>(f: (env: P & I & S & M & F & Su & Q) => T): Rule<P, I, S, M, F & {
|
|
@@ -550,9 +591,12 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
|
|
|
550
591
|
}): Record<string, number>;
|
|
551
592
|
encodeInput(values: {
|
|
552
593
|
[k in keyof I]: I[k] extends MapRef ? ReadonlySet<number> | readonly number[] | ReadonlyMap<number, number> : I[k] extends CarriedList<any, any> ? readonly Raw[] : Raw;
|
|
594
|
+
}, params?: {
|
|
595
|
+
[k in keyof P]: Raw;
|
|
553
596
|
}): Request;
|
|
554
597
|
randomParams(rand: Rand, bound: number): Record<string, number>;
|
|
555
|
-
|
|
598
|
+
private paramValues;
|
|
599
|
+
randomInput(rand: Rand, bound: number, ps?: Record<string, number>): Request;
|
|
556
600
|
decode(st: State): Record<string, Raw>;
|
|
557
601
|
get orderedBy(): string | undefined;
|
|
558
602
|
interpreter(): {
|
|
@@ -754,6 +798,15 @@ export declare class FoldRule<P extends Obj = {}, I extends Obj = {}, S extends
|
|
|
754
798
|
node(f: (env: P & I, left: S, right: S) => {
|
|
755
799
|
[k in keyof S]: TermLike;
|
|
756
800
|
}): FoldRule<P, I, S>;
|
|
801
|
+
children<A extends Record<string, number | ((p: P) => TermLike)>>(from: A, step: (acc: {
|
|
802
|
+
[k in keyof A]: Term;
|
|
803
|
+
}, kid: S, p: P) => {
|
|
804
|
+
[k in keyof A]: TermLike;
|
|
805
|
+
}, lemmas?: Record<string, (acc: {
|
|
806
|
+
[k in keyof A]: Term;
|
|
807
|
+
}, p: P) => Cond>): RoseFold<P, I, S, {
|
|
808
|
+
[k in keyof A]: Term;
|
|
809
|
+
}>;
|
|
757
810
|
private env;
|
|
758
811
|
private names;
|
|
759
812
|
private side;
|
|
@@ -774,4 +827,49 @@ export declare class FoldRule<P extends Obj = {}, I extends Obj = {}, S extends
|
|
|
774
827
|
}): Agreement;
|
|
775
828
|
}
|
|
776
829
|
export declare const fold: (name: string) => FoldRule;
|
|
777
|
-
export declare const proveFold: (f:
|
|
830
|
+
export declare const proveFold: (f: {
|
|
831
|
+
fold: Fold;
|
|
832
|
+
lemmas?: Law[];
|
|
833
|
+
}, laws: Law[]) => Verdict;
|
|
834
|
+
export type RoseValue<I> = {
|
|
835
|
+
leaf: I;
|
|
836
|
+
} | {
|
|
837
|
+
node: I;
|
|
838
|
+
children: RoseValue<I>[];
|
|
839
|
+
};
|
|
840
|
+
type RoseSpec = {
|
|
841
|
+
from: Record<string, number | ((p: never) => TermLike)>;
|
|
842
|
+
step: (acc: never, kid: never, p: never) => Record<string, TermLike>;
|
|
843
|
+
lemmas: Record<string, (acc: never, p: never) => Cond>;
|
|
844
|
+
};
|
|
845
|
+
export declare class RoseFold<P extends Obj = {}, I extends Obj = {}, S extends Obj = {}, A extends Obj = {}> {
|
|
846
|
+
private readonly spec;
|
|
847
|
+
private readonly kids;
|
|
848
|
+
private readonly nodeOf?;
|
|
849
|
+
constructor(spec: FoldSpec, kids: RoseSpec, nodeOf?: ((env: never, kids: never) => Record<string, TermLike>) | undefined);
|
|
850
|
+
get name(): string;
|
|
851
|
+
node(f: (env: P & I, kids: A) => {
|
|
852
|
+
[k in keyof S]: TermLike;
|
|
853
|
+
}): RoseFold<P, I, S, A>;
|
|
854
|
+
private env;
|
|
855
|
+
private names;
|
|
856
|
+
private side;
|
|
857
|
+
private accs;
|
|
858
|
+
private start;
|
|
859
|
+
law(name: string, f: (env: P & S) => Cond): Law;
|
|
860
|
+
get lemmas(): Law[];
|
|
861
|
+
get fold(): Fold;
|
|
862
|
+
run(tree: RoseValue<{
|
|
863
|
+
[k in keyof I]: Raw;
|
|
864
|
+
}>, params?: {
|
|
865
|
+
[k in keyof P]?: Raw;
|
|
866
|
+
}): {
|
|
867
|
+
[k in keyof S]: number;
|
|
868
|
+
};
|
|
869
|
+
crossCheck(laws?: Law[], opts?: {
|
|
870
|
+
trials?: number;
|
|
871
|
+
depth?: number;
|
|
872
|
+
bound?: number;
|
|
873
|
+
rand?: Rand;
|
|
874
|
+
}): Agreement;
|
|
875
|
+
}
|