gdp-wired 0.14.2 → 0.16.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.
@@ -299,6 +299,26 @@ export declare class ListRef {
299
299
  within(): Forall;
300
300
  increasing(): Forall;
301
301
  }
302
+ export declare class SeqRef {
303
+ private readonly map;
304
+ readonly len: Term;
305
+ private readonly base?;
306
+ constructor(map: MapRef, len: Term, base?: Term | undefined);
307
+ at(i: TermLike): Term;
308
+ private agree;
309
+ prefixOf(o: SeqRef): Cond;
310
+ startsWith(o: SeqRef): Cond;
311
+ equals(o: SeqRef): Cond;
312
+ extends(o: SeqRef, by: TermLike): Cond;
313
+ has(x: TermLike): Cond;
314
+ }
315
+ export declare class SeqTable {
316
+ readonly name: string;
317
+ readonly rows: Term;
318
+ constructor(name: string, rows: Term);
319
+ row(r: TermLike): SeqRef;
320
+ }
321
+ export declare const everyBelow: (n: TermLike, f: (key: Term) => Cond) => Cond;
302
322
  export type PartsWrite = {
303
323
  readonly parts: Record<string, MapWrite>;
304
324
  };
@@ -440,6 +460,14 @@ export type RecurDef<A extends string, U extends Record<string, FnDef>> = {
440
460
  export declare const recur: <const A extends string = never, U extends Record<string, FnDef> = {}>(name: string, def: RecurDef<A, U>, lemmas?: Record<string, (x: Term, fx: Term, env: RecurEnv<A, U>) => Cond>, between?: Record<string, (x: Term, fx: Term, y: Term, fy: Term, env: RecurEnv<A, U>) => Cond>) => FnDef & {
441
461
  verdict: Verdict;
442
462
  };
463
+ type RecurLemma = (x: Term, fx: Term, env: Record<string, Term>) => Cond;
464
+ type RecurBetween = (x: Term, fx: Term, y: Term, fy: Term, env: Record<string, Term>) => Cond;
465
+ export declare const recurOf: (d: FnDef) => {
466
+ name: string;
467
+ def: RecurDef<string, Record<string, FnDef>>;
468
+ lemmas: Record<string, RecurLemma>;
469
+ between: Record<string, RecurBetween>;
470
+ } | undefined;
443
471
  type Kinds = Record<string, Kind<unknown>>;
444
472
  type ValuesOf<K extends Kinds> = {
445
473
  [n in keyof K]: K[n] extends Kind<infer V> ? V : never;
@@ -465,6 +493,8 @@ type Spec = {
465
493
  nodes?: [string, Kind<unknown>, string[], Record<string, NodeFold<unknown, string>>][];
466
494
  kinded?: [string, Kind<unknown>][];
467
495
  carried?: string[];
496
+ seqs?: string[];
497
+ seqTables?: string[];
468
498
  lists2?: [string, Kind<unknown>, Record<string, ListFold<unknown>>, Record<string, ListLemma>][];
469
499
  fns?: [string, FnDef][];
470
500
  facts?: ((env: never) => Record<string, Cond | Forall>)[];
@@ -487,6 +517,12 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
487
517
  constructor(spec: Spec);
488
518
  private with;
489
519
  get name(): string;
520
+ get parts(): {
521
+ params: [string, Kind<unknown>][];
522
+ fns: [string, FnDef][];
523
+ lists: [string, Kind<unknown>, Record<string, ListFold<unknown>>, Record<string, ListLemma>][];
524
+ };
525
+ withParts(fns: Record<string, FnDef>, listLemmas?: Record<string, Record<string, ListLemma>>): this;
490
526
  params<K extends Kinds>(kinds: K): Rule<P & ValuesOf<K>, I, S, M, F, R, Su, Q>;
491
527
  inputs<K extends Kinds>(kinds: K): Rule<P, I & ValuesOf<K>, S, M, F, R, Su, Q>;
492
528
  state<T extends Record<string, number | ((p: P) => TermLike) | Init<unknown>>>(init: T): Rule<P, I, S & {
@@ -525,6 +561,12 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
525
561
  carries<const N extends string>(...names: N[]): Rule<P, I & {
526
562
  [k in N]: MapRef;
527
563
  }, S, M, F, R, Su, Q>;
564
+ carriesSeq<const N extends string>(...names: N[]): Rule<P, I & {
565
+ [k in N]: SeqRef;
566
+ }, S, M, F, R, Su, Q>;
567
+ carriesSeqs<const N extends string>(...names: N[]): Rule<P, I & {
568
+ [k in N]: SeqTable;
569
+ }, S, M, F, R, Su, Q>;
528
570
  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 & {
529
571
  [k in N]: CarriedList<V, Fd>;
530
572
  }, S, M, F, R, Su, Q>;
@@ -590,7 +632,7 @@ export declare class Rule<P extends Obj = {}, I extends Obj = {}, S extends Obj
590
632
  [k in keyof P]: Raw;
591
633
  }): Record<string, number>;
592
634
  encodeInput(values: {
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;
635
+ [k in keyof I]: I[k] extends MapRef ? ReadonlySet<number> | readonly number[] | ReadonlyMap<number, number> : I[k] extends SeqRef ? readonly number[] : I[k] extends SeqTable ? readonly (readonly number[])[] : I[k] extends CarriedList<any, any> ? readonly Raw[] : Raw;
594
636
  }, params?: {
595
637
  [k in keyof P]: Raw;
596
638
  }): Request;
@@ -639,6 +681,10 @@ export type Decision = {
639
681
  given: C[];
640
682
  then: C;
641
683
  goal: Goal;
684
+ steps?: {
685
+ rule: Rule<Obj>;
686
+ law: StepLaw;
687
+ };
642
688
  };
643
689
  type ResultOf<V> = 0 extends 1 & V ? Term : [V] extends [Cond] ? Cond : [V] extends [Decimal] ? Decimal : [V] extends [Int] ? Int : [V] extends [Choice<infer K>] ? Choice<K> : [V] extends [Str] ? Str : [V] extends [Variant<infer C, infer T>] ? Variant<C, T> : [V] extends [Maybe<infer W>] ? Maybe<ResultOf<W>> : [V] extends [Term | number] ? Term : [V] extends [Record<string, unknown>] ? {
644
690
  [f in keyof V]: ResultOf<V[f]>;
@@ -660,7 +706,7 @@ type Value<T> = {
660
706
  [k in keyof T]: ValueOf<T[k]>;
661
707
  };
662
708
  type RawInputs<T> = {
663
- [k in keyof T]: T[k] extends MapRef ? ReadonlySet<number> | readonly number[] | ReadonlyMap<number, number> : T[k] extends CarriedList<infer V, infer _F> ? readonly ValueOf<V>[] : ValueOf<T[k]>;
709
+ [k in keyof T]: T[k] extends MapRef ? ReadonlySet<number> | readonly number[] | ReadonlyMap<number, number> : T[k] extends SeqRef ? readonly number[] : T[k] extends SeqTable ? readonly (readonly number[])[] : T[k] extends CarriedList<infer V, infer _F> ? readonly ValueOf<V>[] : ValueOf<T[k]>;
664
710
  };
665
711
  type Field = Term | Cond | Int | Decimal | Choice<string> | Str | Variant<any, any> | Maybe<any> | {
666
712
  [f: string]: Field | number;
@@ -752,13 +798,30 @@ export type Verdict = {
752
798
  params: Record<string, number>;
753
799
  trace: Record<string, unknown>[];
754
800
  };
801
+ found?: Found;
802
+ };
803
+ export type Found = {
804
+ laws?: {
805
+ law: Law;
806
+ text: string;
807
+ source?: string;
808
+ }[];
809
+ lemmas?: Record<string, {
810
+ lemmas?: string[];
811
+ between?: string[];
812
+ }>;
813
+ };
814
+ export type ProveOptions = {
815
+ search?: boolean | {
816
+ ms?: number;
817
+ };
755
818
  };
756
819
  export declare class Unproven extends Error {
757
820
  readonly verdict: Verdict;
758
821
  constructor(what: string, verdict: Verdict);
759
822
  }
760
- export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[]): Verdict;
761
- export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[]): Verdict;
823
+ export declare function proveRule(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], opts?: ProveOptions): Verdict;
824
+ export declare function proveSteps(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], stepLaws: StepLaw[], opts?: ProveOptions): Verdict;
762
825
  export type { StepLaw };
763
826
  export type { Liveness };
764
827
  export declare function proveLeadsTo(r: Rule<Obj, Obj, Obj, Obj, Obj, Obj, Obj, Obj>, laws: Law[], l: Liveness): Verdict;
package/dist/core/dsl.js CHANGED
@@ -19,6 +19,8 @@ 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 } from "../kernel/prove.js";
22
+ import { discover } from "./discover.js";
23
+ import { inferLaws } from "./infer.js";
22
24
  // --- terms and conditions -------------------------------------------------------------------------------------------
23
25
  const naturalConst = (v) => {
24
26
  if (!Number.isSafeInteger(v) || v < 0)
@@ -860,6 +862,43 @@ export class ListRef {
860
862
  within() { return forall(k => this.len.atMost(k).implies(this.at(k).eq(0))); }
861
863
  increasing() { return forall((i, j) => i.lt(j).and(j.lt(this.len)).implies(this.at(i).lt(this.at(j)))); }
862
864
  }
865
+ // A sequence of naturals as positions and a length (a path's segments, as interned names): `at(i)` its value at i,
866
+ // `len` its length. Comparing two is a condition about every position below a length (the array property fragment,
867
+ // decidable), so a law about prefixes needs no induction. One a request carries (`carriesSeq`), or a row of a table of
868
+ // them (`carriesSeqs`), its positions read from its row's offset in the table's one map.
869
+ export class SeqRef {
870
+ map;
871
+ len;
872
+ base;
873
+ constructor(map, len, base) {
874
+ this.map = map;
875
+ this.len = len;
876
+ this.base = base;
877
+ }
878
+ at(i) { return this.map.at(!this.base ? i : typeof i === 'number' ? this.base.plus(i) : i.plus(this.base)); }
879
+ // The same values at every position below `upTo` (both have them).
880
+ agree(o, upTo) { return everyKey(k => k.lt(upTo).implies(this.at(k).eq(o.at(k)))); }
881
+ // This one is where o starts; o is where this one starts; the same; o followed by `by` more; x at some position.
882
+ prefixOf(o) { return this.len.atMost(o.len).and(this.agree(o, this.len)); }
883
+ startsWith(o) { return o.prefixOf(this); }
884
+ equals(o) { return this.len.eq(o.len).and(this.agree(o, this.len)); }
885
+ extends(o, by) { return o.prefixOf(this).and(this.len.eq(o.len.plus(by))); }
886
+ has(x) { return everyKey(k => k.lt(this.len).implies(this.at(k).ne(x))).not(); }
887
+ }
888
+ // A table of sequences a request carries (each grant's path, say): `row(r)` is row r's sequence, `rows` how many. Rows
889
+ // are read by their number as a map's key, so a condition about every row reads a row only there (`everyBelow(T.rows,
890
+ // r => ...)`).
891
+ export class SeqTable {
892
+ name;
893
+ rows;
894
+ constructor(name, rows) {
895
+ this.name = name;
896
+ this.rows = rows;
897
+ }
898
+ row(r) { return new SeqRef(new MapRef(this.name, 0), new MapRef(`${this.name}_len`, 0).at(r), new MapRef(`${this.name}_base`, 0).at(r)); }
899
+ }
900
+ // f at every key below n: what bookkeeping a request carries meets, row by row (where each grant went, say).
901
+ export const everyBelow = (n, f) => everyKey(k => k.lt(n).implies(f(k)));
863
902
  const when_ = (c, then, otherwise) => new Term({ op: 'if', c: c.c, then: termOf(then), else: termOf(otherwise) }, c.ok === undefined ? undefined : valOf(c.ok ? then : otherwise));
864
903
  const isPartsWrite = (u) => typeof u === 'object' && u !== null && 'parts' in u;
865
904
  // The parts of a value written at a cell, by the names of the maps they go to.
@@ -995,7 +1034,7 @@ export class CarriedList {
995
1034
  .state({ i: 0, ...Object.fromEntries(folds.map(([f, d]) => [`acc$${f}`, typeof d.from === 'number' ? d.from : (p) => d.from(p)])) })
996
1035
  .step((env) => ({ i: env.i.plus(1), ...Object.fromEntries(folds.map(([f, d]) => [`acc$${f}`, d.step(env[`acc$${f}`], item(env.i), env)])) }));
997
1036
  const accs = (env) => Object.fromEntries(folds.map(([f]) => [f, env[`acc$${f}`]]));
998
- return proveRule(r, Object.entries(this.lemmas).map(([n, l]) => r.law(n, (env) => l(accs(env), env.i, env))));
1037
+ return proveRule(r, Object.entries(this.lemmas).map(([n, l]) => r.law(n, (env) => l(accs(env), env.i, env))), { search: false });
999
1038
  }
1000
1039
  // Every carried map it is kept in: the items' parts, then each fold's.
1001
1040
  get maps() { return [...this.kind.vars(this.name), ...Object.keys(this.folds).map(f => `${this.name}$${f}`)]; }
@@ -1374,7 +1413,7 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
1374
1413
  const r = r0.state({ x: 0, fx: ((p) => (zeroReads ? p.fx0 : zeroAt(args.map(a => p[a])))) })
1375
1414
  .step((env) => ({ x: env.x.plus(1), fx: def.next(env.x, env.fx, envAt(args.map(a => env[a]))) }));
1376
1415
  const laws = Object.entries(lemmas).map(([n, f]) => r.law(n, (env) => f(env.x, env.fx, envAt(args.map(a => env[a])))));
1377
- verdict = proveRule(r, laws);
1416
+ verdict = proveRule(r, laws, { search: false });
1378
1417
  failed('lemmas', verdict);
1379
1418
  proofs.lemmas = { machine: r.machine, laws };
1380
1419
  }
@@ -1411,13 +1450,17 @@ export const recur = (name, def, lemmas = {}, between = {}) => {
1411
1450
  .step((env) => ({ y: env.y.plus(1), fy: def.next(env.y, env.fy, envAt(at(env))) }));
1412
1451
  const laws = [...Object.entries(lemmas).map(([n, l]) => r.law(`${n} at y`, (env) => l(env.y, env.fy, envAt(at(env))))),
1413
1452
  ...Object.entries(between).map(([n, l]) => r.law(n, (env) => l(env.x, env.fx, env.y, env.fy, envAt(at(env)))))];
1414
- const v = proveRule(r, laws);
1453
+ const v = proveRule(r, laws, { search: false });
1415
1454
  failed('laws between two points', v);
1416
1455
  proofs.between = { machine: r.machine, laws };
1417
1456
  verdict = { proven: true, checked: verdict.checked && v.checked };
1418
1457
  }
1419
- return { ...base, verdict, facts: facts(true) };
1458
+ const out = { ...base, verdict, facts: facts(true) };
1459
+ recurDefs.set(out, { name, def: def, lemmas: lemmas, between: between });
1460
+ return out;
1420
1461
  };
1462
+ const recurDefs = new WeakMap();
1463
+ export const recurOf = (d) => recurDefs.get(d);
1421
1464
  const distinctNames = (what, names) => { if (new Set(names).size !== names.length)
1422
1465
  throw new Error(`${what}: a name given twice`); };
1423
1466
  // A recurrence as JavaScript: a function of its arguments, the functions it reads inlined (each called on its arguments,
@@ -1527,6 +1570,21 @@ export class Rule {
1527
1570
  }
1528
1571
  with(more) { return new Rule({ ...this.spec, ...more }); }
1529
1572
  get name() { return this.spec.name; }
1573
+ // For core/discover.ts: the functions and carried lists the rule reads, and the same rule with functions made again
1574
+ // and lemmas added to its lists' folds (each proven here, as carriesList proves its own).
1575
+ get parts() {
1576
+ return { params: this.spec.params, fns: this.spec.fns ?? [], lists: this.spec.lists2 ?? [] };
1577
+ }
1578
+ withParts(fns, listLemmas = {}) {
1579
+ const lists = (this.spec.lists2 ?? []).map(([n, k, fs, ls]) => [n, k, fs, { ...ls, ...(listLemmas[n] ?? {}) }]);
1580
+ for (const [n, k, fs, ls] of lists)
1581
+ if (listLemmas[n]) {
1582
+ const v2 = new CarriedList(n, k, v(`${n}_len`), fs, part => new MapRef(part, 0), ls, undefined, this.spec.params).prove();
1583
+ if (!(v2.proven && v2.checked))
1584
+ throw new Unproven(`${n}'s fold lemmas`, v2);
1585
+ }
1586
+ return this.with({ fns: (this.spec.fns ?? []).map(([n, d]) => [n, fns[n] ?? d]), lists2: lists });
1587
+ }
1530
1588
  params(kinds) { return this.with({ params: [...this.spec.params, ...Object.entries(kinds)] }); }
1531
1589
  inputs(kinds) { return this.with({ inputs: [...this.spec.inputs, ...Object.entries(kinds)] }); }
1532
1590
  // Each variable starts at a natural or at a term over the params, or is declared by a kind and starts where `from`
@@ -1612,6 +1670,16 @@ export class Rule {
1612
1670
  facts(f) { return this.with({ facts: [...(this.spec.facts ?? []), f] }); }
1613
1671
  // Maps each request carries whole (a set of names, say: cells 0 or 1), every cell past them 0; read like any map.
1614
1672
  carries(...names) { return this.with({ carried: [...(this.spec.carried ?? []), ...names] }); }
1673
+ // Sequences each request carries (SeqRef): `.carriesSeq('Path')`, then `Path.at(i)`, `Path.len`, `A.prefixOf(B)`; the
1674
+ // request gives each as an array of naturals.
1675
+ carriesSeq(...names) {
1676
+ return this.with({ carried: [...(this.spec.carried ?? []), ...names], inputs: [...this.spec.inputs, ...names.map(n => [`${n}_len`, nat])], seqs: [...(this.spec.seqs ?? []), ...names] });
1677
+ }
1678
+ // Tables of sequences each request carries (SeqTable): `.carriesSeqs('Paths')`, then `Paths.row(r)`, `Paths.rows`; the
1679
+ // request gives each as an array of arrays of naturals.
1680
+ carriesSeqs(...names) {
1681
+ return this.with({ carried: [...(this.spec.carried ?? []), ...names.flatMap(n => [n, `${n}_base`, `${n}_len`])], inputs: [...this.spec.inputs, ...names.map(n => [`${n}_rows`, nat])], seqTables: [...(this.spec.seqTables ?? []), ...names] });
1682
+ }
1615
1683
  // A list of values of a kind each request carries, with folds over it and lemmas about the folds (CarriedList):
1616
1684
  // .carriesList('Lines', record({amount: nat}), {total: {from: 0, step: (acc, line) => acc.plus(line.amount)}})
1617
1685
  // Each fold's start and step, and each lemma, may read the params declared before it.
@@ -1758,6 +1826,8 @@ export class Rule {
1758
1826
  // A decision: whenever the params and the request meet `given`, `then` holds. Decisions don't read the state; the
1759
1827
  // functions' facts are assumed at the arguments the decision reads them at, and a carried list's equations (each
1760
1828
  // fold's start, its step and its lemmas) at the list's end, the key before it and every key the decision reads it at.
1829
+ // A condition about every key (`everyKey`, `subsetOf`, ...) in `given` or `then` makes the decision a step law of
1830
+ // this rule (its conditions as flags, the step left as it is): the flags' quantifiers are what the kernel closes.
1761
1831
  decide(name, given, then) {
1762
1832
  const env = this.env({ flags: false, state: false, queries: false });
1763
1833
  const g = given(env), hyps = [...this.kindAssumptions(), ...this.spec.pre.map(f => f(env).c), ...this.spec.assume.map(f => f(env).c), ...(Array.isArray(g) ? g : [g]).map(c => c.c)];
@@ -1766,7 +1836,14 @@ export class Rule {
1766
1836
  const listed = lists.flatMap(l => l.equationsAt(uniqueTerms([l.len, l.len.minus(1), ...keysRead([...hyps, t], l.maps)])).map(c => c.c));
1767
1837
  const goal = { name, hyps: [...listed, ...hyps, { op: 'not', x: t }] };
1768
1838
  const facts = this.spec.fns?.length || this.spec.facts?.length ? factsFor(this.machine, goal) : [];
1769
- return withSource({ name, rule: this, given: hyps, then: t, goal: { name, hyps: [...facts, ...goal.hyps] } }, { decision: { given: given, then: then, own: Array.isArray(g) ? g.length : 1 } });
1839
+ const d = { name, rule: this, given: hyps, then: t, goal: { name, hyps: [...facts, ...goal.hyps] } };
1840
+ if ([...(Array.isArray(g) ? g : [g]).map(c => c.c), t].some(quantifiedIn)) {
1841
+ const conds = (e) => { const own = given(e); return { given$: all(...(Array.isArray(own) ? own : [own])), then$: then(e) }; };
1842
+ const r = (this.spec.step ? this : this.step(() => ({}))).flags(conds);
1843
+ const flag = (f) => (env) => env[f];
1844
+ d.steps = { rule: r, law: r.stepLaw(name, flag('then$'), flag('given$')) };
1845
+ }
1846
+ return withSource(d, { decision: { given: given, then: then, own: Array.isArray(g) ? g.length : 1 } });
1770
1847
  }
1771
1848
  // A decision about two requests under the same params (noninterference, say: requests that differ only in a secret
1772
1849
  // are decided alike). `given` and `then` read both; the second request's inputs are named b.<input> in a witness.
@@ -1818,6 +1895,15 @@ export class Rule {
1818
1895
  out[n] = k.value(n);
1819
1896
  for (const n of this.spec.carried ?? [])
1820
1897
  out[n] = new MapRef(n, 0);
1898
+ for (const n of this.spec.seqs ?? []) {
1899
+ delete out[`${n}_len`];
1900
+ out[n] = new SeqRef(new MapRef(n, 0), v(`${n}_len`));
1901
+ }
1902
+ for (const n of this.spec.seqTables ?? []) {
1903
+ for (const part of [`${n}_base`, `${n}_len`, `${n}_rows`])
1904
+ delete out[part];
1905
+ out[n] = new SeqTable(n, v(`${n}_rows`));
1906
+ }
1821
1907
  const ps = Object.fromEntries(this.spec.params.map(([n]) => [n, out[n]]));
1822
1908
  for (const [n, k, fs, ls] of this.spec.lists2 ?? []) {
1823
1909
  const l = new CarriedList(n, k, v(`${n}_len`), fs, part => new MapRef(part, 0), ls, ps, this.spec.params);
@@ -2159,16 +2245,35 @@ export class Rule {
2159
2245
  // --- running it -----------------------------------------------------------------------------------------------------
2160
2246
  // Runtime values (numbers, booleans, choice names, null for a missing value) to the variables the machine reads.
2161
2247
  encodeParams(values) { return encode(this.spec.params, values); }
2162
- // A carried map is a Set of keys (each cell 1), an array of them, or a Map of key to natural.
2248
+ // A carried map is a Set of keys (each cell 1), an array of them, or a Map of key to natural; a sequence an array of
2249
+ // naturals, a table of them an array of such arrays.
2163
2250
  // A carried list is an array of its items' runtime values (its length and folds computed from them); `params`, what
2164
2251
  // folds that read the params are computed with.
2165
2252
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2166
2253
  encodeInput(values, params) {
2167
2254
  const at = params && this.paramValues(this.encodeParams(params));
2168
2255
  const lists = Object.fromEntries((this.spec.lists2 ?? []).map(([n, k, fs]) => [n, new CarriedList(n, k, v(`${n}_len`), fs, part => new MapRef(part, 0), {}, at).encode(values[n])]));
2169
- const out = encode(this.spec.inputs, { ...values, ...Object.fromEntries(Object.entries(lists).map(([n, l]) => [`${n}_len`, l.len])) });
2256
+ // A sequence: its values at their positions (no 0 cell), its length; a table: the rows one after another in one map,
2257
+ // each row's offset and length.
2258
+ const seqMaps = {}, seqLens = {};
2259
+ const cells = (xs, from = 0) => xs.flatMap((x, i) => (x ? [[from + i, x]] : []));
2260
+ for (const n of this.spec.seqs ?? []) {
2261
+ const xs = values[n] ?? [];
2262
+ seqMaps[n] = new Map(cells(xs));
2263
+ seqLens[`${n}_len`] = xs.length;
2264
+ }
2265
+ for (const n of this.spec.seqTables ?? []) {
2266
+ const rows = values[n] ?? [], base = [];
2267
+ rows.reduce((at, r) => (base.push(at), at + r.length), 0);
2268
+ seqMaps[n] = new Map(rows.flatMap((r, i) => cells(r, base[i])));
2269
+ seqMaps[`${n}_base`] = new Map(base.flatMap((b, i) => (b ? [[i, b]] : [])));
2270
+ seqMaps[`${n}_len`] = new Map(rows.flatMap((r, i) => (r.length ? [[i, r.length]] : [])));
2271
+ seqLens[`${n}_rows`] = rows.length;
2272
+ }
2273
+ const out = encode(this.spec.inputs, { ...values, ...seqLens, ...Object.fromEntries(Object.entries(lists).map(([n, l]) => [`${n}_len`, l.len])) });
2170
2274
  for (const l of Object.values(lists))
2171
2275
  Object.assign(out, l.maps);
2276
+ Object.assign(out, seqMaps);
2172
2277
  for (const n of this.spec.carried ?? []) {
2173
2278
  if (n in out)
2174
2279
  continue;
@@ -2249,6 +2354,8 @@ const expandParts = (updates) => {
2249
2354
  };
2250
2355
  const encode = (kinds, values) => Object.assign({}, ...kinds.map(([n, k]) => k.encode(n, values[n])));
2251
2356
  export const rule = (name) => new Rule({ name, params: [], inputs: [], state: [], maps: [], flags: [], pre: [], assume: [], sums: [], queries: [] });
2357
+ const quantifiedE = (e) => e.op === 'if' ? quantifiedIn(e.c) || quantifiedE(e.then) || quantifiedE(e.else) : 'a' in e && typeof e.a === 'object' ? quantifiedE(e.a) || ('b' in e && typeof e.b === 'object' && quantifiedE(e.b)) : false;
2358
+ const quantifiedIn = (c) => c.op === 'all' || (c.op === 'not' ? quantifiedIn(c.x) : c.op === 'and' || c.op === 'or' ? c.xs.some(quantifiedIn) : c.op === 'flag' ? false : quantifiedE(c.a) || quantifiedE(c.b));
2252
2359
  // The kind a result field is kept in.
2253
2360
  const kindOf = (t) => (t instanceof Cond ? bool : t instanceof Int ? int : t instanceof Decimal ? decimal(t.places) : t instanceof Choice ? oneOf(...t.values) : t instanceof Str ? str({ like: t.patterns })
2254
2361
  : t instanceof Variant ? t.kind : t instanceof Maybe ? maybe(kindOf(t.inner)) : isRecord(t) ? record(Object.fromEntries(Object.entries(t).map(([f, x]) => [f, kindOf(x)]))) : nat);
@@ -2402,14 +2509,16 @@ const withKinds = (r, laws) => [...r.kindLaws().filter(k => !laws.some(l => l.na
2402
2509
  // interpreter took. A rule that reads functions is skipped: the kernel knows them only by their facts (a recurrence's
2403
2510
  // induction machine reads the function it proves things about), so a run computing them proves nothing about the goal.
2404
2511
  const refuteBudget = { trials: 60, depth: 6, bound: 3, ms: 150 };
2405
- function refuteByRuns(r, laws, stepLaws = []) {
2512
+ // The search's own runs, before it looks for what the laws need: deeper and wider, from another seed.
2513
+ const deeperBudget = { trials: 600, depth: 24, bound: 8, ms: 1500, seed: 29 };
2514
+ function refuteByRuns(r, laws, stepLaws = [], budget = refuteBudget) {
2406
2515
  const m = r.machine;
2407
2516
  if (Object.keys(m.fns ?? {}).length || m.facts?.length)
2408
2517
  return undefined;
2409
- const run = interpret(m), rand = seeded(13), until = performance.now() + refuteBudget.ms, bound = refuteBudget.bound;
2518
+ const run = interpret(m), rand = seeded(budget.seed ?? 13), until = performance.now() + budget.ms, bound = budget.bound;
2410
2519
  const shown = (input) => Object.fromEntries(Object.entries(input).map(([n, x]) => [n, typeof x === 'number' ? x : Object.fromEntries(x)]));
2411
2520
  try {
2412
- for (let trial = 0; trial < refuteBudget.trials && performance.now() < until; trial++) {
2521
+ for (let trial = 0; trial < budget.trials && performance.now() < until; trial++) {
2413
2522
  const params = r.randomParams(rand, bound);
2414
2523
  if (m.pre && !evalC(m.pre, params, { scalars: {}, maps: {} }, m))
2415
2524
  continue;
@@ -2421,7 +2530,7 @@ function refuteByRuns(r, laws, stepLaws = []) {
2421
2530
  for (const { w, on } of watching)
2422
2531
  if (!('then' in w) && !on.start(st))
2423
2532
  return broken(w, '@start', st);
2424
- for (let d = 0, tries = 0; d < refuteBudget.depth && tries < 40; tries++) {
2533
+ for (let d = 0, tries = 0; d < budget.depth && tries < 40; tries++) {
2425
2534
  const input = r.randomInput(rand, bound, params);
2426
2535
  if (!allowed(m, run, params, st, input))
2427
2536
  continue;
@@ -2441,7 +2550,7 @@ function refuteByRuns(r, laws, stepLaws = []) {
2441
2550
  // variable of a kind is drawn as its kind draws (33 yes/nos drawn from 0..7 almost never make a lawful state).
2442
2551
  const steps1 = stepLaws.filter(sl => (sl.steps ?? 1) === 1), given = withKinds(r, laws);
2443
2552
  const wide = Object.keys(m.state).length + m.inputs.length >= 32, lawsHere = wide ? given : [], kinded = wide ? r.kindsOfState() : [];
2444
- for (let trial = 0; !m.history && (steps1.length || lawsHere.length) && trial < refuteBudget.trials * 4 && performance.now() < until; trial++) {
2553
+ for (let trial = 0; !m.history && (steps1.length || lawsHere.length) && trial < budget.trials * 4 && performance.now() < until; trial++) {
2445
2554
  const params = r.randomParams(rand, bound);
2446
2555
  if (m.pre && !evalC(m.pre, params, { scalars: {}, maps: {} }, m))
2447
2556
  continue;
@@ -2469,25 +2578,56 @@ function refuteByRuns(r, laws, stepLaws = []) {
2469
2578
  return undefined;
2470
2579
  }
2471
2580
  const refutedVerdict = (out) => ({ proven: false, checked: false, goal: out.goal, stuck: 'sat', witness: out.witness, ...(out.run ? { run: out.run } : {}) });
2472
- export function proveRule(r, laws) {
2581
+ export function proveRule(r, laws, opts = {}) {
2473
2582
  const refuted = refuteByRuns(r, laws);
2474
2583
  if (refuted)
2475
2584
  return refutedVerdict(refuted);
2476
2585
  const out = proveLaws(r.machine, withKinds(r, laws));
2477
- 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 } : {}) } : {}) };
2586
+ const v = { 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 } : {}) } : {}) };
2587
+ return searched(r, laws, [], v, opts);
2478
2588
  }
2479
- export function proveSteps(r, laws, stepLaws) {
2589
+ export function proveSteps(r, laws, stepLaws, opts = {}) {
2480
2590
  const refuted = refuteByRuns(r, laws, stepLaws);
2481
2591
  if (refuted)
2482
2592
  return refutedVerdict(refuted);
2483
2593
  const out = proveStepLaws(r.machine, withKinds(r, laws), stepLaws);
2484
- 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 } : {}) } : {}) };
2594
+ const v = { 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 } : {}) } : {}) };
2595
+ return searched(r, laws, stepLaws, v, opts);
2596
+ }
2597
+ // The search, where the prover alone didn't prove the laws and nothing refuted them: the lemmas first where the rule has
2598
+ // recurrences or carried lists (the rule made again with them), then invariants over what it then is. The verdict is the
2599
+ // prover's on the laws with what was found; when nothing is found, the prover's own (with why).
2600
+ function searched(r, laws, stepLaws, v, opts) {
2601
+ if ((v.proven && v.checked) || opts.search === false || v.run || v.stuck === 'sat' && !/@step|@\d+ steps/.test(v.goal ?? ''))
2602
+ return v;
2603
+ const deeper = refuteByRuns(r, laws, stepLaws, deeperBudget);
2604
+ if (deeper?.run)
2605
+ return refutedVerdict(deeper);
2606
+ const until = performance.now() + (typeof opts.search === 'object' && opts.search.ms !== undefined ? opts.search.ms : 10_000);
2607
+ let rule = r, lemmas;
2608
+ const { fns, lists } = r.parts;
2609
+ if (lists.length || fns.some(([, d]) => recurOf(d))) {
2610
+ const d = discover(r, laws, stepLaws, v);
2611
+ if (d.verdict.proven && d.verdict.checked)
2612
+ return { ...d.verdict, found: { lemmas: d.found } };
2613
+ if (Object.keys(d.found).length)
2614
+ (rule = d.rule, lemmas = d.found);
2615
+ }
2616
+ const ms = until - performance.now();
2617
+ if (ms < 500)
2618
+ return v;
2619
+ const inf = inferLaws(rule, laws, stepLaws, { ms, ...(rule === r ? { first: v } : {}) });
2620
+ if (!(inf.proven && inf.checked))
2621
+ return { ...v, why: [v.why, `the search found nothing that proves it in ${Math.round(inf.stats.ms)} ms`].filter(Boolean).join('; ') };
2622
+ const { found, stats: _stats, ...verdict } = inf;
2623
+ void _stats;
2624
+ return { ...verdict, found: { laws: found, ...(lemmas ? { lemmas } : {}) } };
2485
2625
  }
2486
2626
  export function proveLeadsTo(r, laws, l) {
2487
2627
  const out = proveLiveness(r.machine, withKinds(r, laws), l);
2488
2628
  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 } : {}) } : {}) };
2489
2629
  }
2490
- export const proveDecision = (d) => goalsVerdict([d.goal]);
2630
+ export const proveDecision = (d) => (d.steps ? proveSteps(d.steps.rule, [], [d.steps.law], { search: false }) : goalsVerdict([d.goal]));
2491
2631
  const seeded = (seed) => { let x = seed; return k => { x = (x * 1103515245 + 12345) % 2 ** 31; return Math.floor(x / 2 ** 31 * k); }; };
2492
2632
  // A decision's callbacks against its condition, on random params and requests: `given` and `then` each run with every
2493
2633
  // value known, against what the interpreter evaluates of the decision built from them.
@@ -0,0 +1,29 @@
1
+ import { type Law, type Machine, type State } from './machine.js';
2
+ import { type StepLaw, type Verdict, proveRule } from './dsl.js';
3
+ type AnyRule = Parameters<typeof proveRule>[0];
4
+ export type InferOptions = {
5
+ ms?: number;
6
+ runsMs?: number;
7
+ seen?: State[];
8
+ constants?: number[];
9
+ maxCandidates?: number;
10
+ first?: Verdict;
11
+ progress?: (phase: string, info: Record<string, number | string>) => void;
12
+ };
13
+ export type Found = {
14
+ law: Law;
15
+ text: string;
16
+ source?: string;
17
+ };
18
+ export type Inferred = Verdict & {
19
+ found: Found[];
20
+ stats: {
21
+ samples: number;
22
+ candidates: number;
23
+ alive: number;
24
+ proofs: number;
25
+ ms: number;
26
+ };
27
+ };
28
+ export declare function inferLaws(target: AnyRule | Machine, laws: Law[], stepLaws?: StepLaw[], opts?: InferOptions): Inferred;
29
+ export {};