gdp-wired 0.20.0 → 0.21.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 +37 -6
- package/dist/core/cycles.d.ts +12 -0
- package/dist/core/cycles.js +7 -0
- package/dist/core/data.d.ts +138 -13
- package/dist/core/data.js +842 -145
- package/dist/core/dsl.d.ts +27 -1
- package/dist/core/dsl.js +137 -26
- package/dist/core/exists.d.ts +26 -0
- package/dist/core/exists.js +625 -0
- package/dist/core/infer.d.ts +13 -0
- package/dist/core/infer.js +258 -41
- package/dist/core/machine.d.ts +27 -0
- package/dist/core/machine.js +45 -0
- package/dist/core/transfer.d.ts +17 -0
- package/dist/core/transfer.js +468 -0
- package/dist/core/watch.js +5 -1
- package/dist/kernel/prove.d.ts +5 -1
- package/dist/kernel/prove.js +11 -5
- package/dist/lean-kernel/emit.d.ts +2 -0
- package/dist/lean-kernel/emit.js +196 -15
- package/dist/lean-kernel/library.js +2 -2
- package/package.json +2 -2
package/dist/lean-kernel/emit.js
CHANGED
|
@@ -369,17 +369,30 @@ theorem ${id}_laws : ∀ maps ρ, evalC ρ maps ${id}_fold.pre = true → ∀ t,
|
|
|
369
369
|
// abbreviation for its column), proven from the machine's theorem by unfolding the law's own definition. The statement
|
|
370
370
|
// is what a Lean development cites; the column numbers stay inside the namespace.
|
|
371
371
|
// `have`: the line that gives each law as a hypothesis hl (an SEntry's Holds by default; a cyclic proof's, holdsOver).
|
|
372
|
+
// A law with "there exists" (core/exists.ts, `ex` on the law the kernel proved) is stated as the existential it is:
|
|
373
|
+
// `<id>_someN`, ∃ w1 .. wn, P with the bound naturals set to them (updAll, Ghost.lean), from the law proven with its
|
|
374
|
+
// witness terms in place (some_of_subst) or over the machine's ghosts at every reachable state of the machine without
|
|
375
|
+
// them (some_of_ghost); and `<id>.lawN` the same over named variables, each bound natural as w1, w2, ... (`lit`: the
|
|
376
|
+
// condition printer, for P's literal).
|
|
372
377
|
export function readable(id, laws, vars, maps, reach) {
|
|
373
378
|
for (const name of [...vars, ...maps])
|
|
374
379
|
if (name === 'ρ' || name === 'μ' || name.includes('«') || name.includes('»'))
|
|
375
380
|
throw new Error(`${id}: the name ${name} can't be a Lean abbreviation here`);
|
|
376
381
|
const q = (name) => `«${name}»`;
|
|
377
|
-
|
|
382
|
+
// Division and remainder by a constant (div$k, mod$k): Lean's x / k and x % k, under the hypothesis that the map is
|
|
383
|
+
// that function (what the facts' theorem, <id>_facts_hold, takes too), rewritten into the statement.
|
|
384
|
+
const arithOf = (map) => { const m = /^(div|mod)\$(\d+)$/.exec(map); return m ? { op: m[1] === 'div' ? '/' : '%', k: Number(m[2]) } : undefined; };
|
|
385
|
+
const arithIn = (law) => [...new Set([...JSON.stringify(law.holds).matchAll(/"op":"get","map":"((?:div|mod)\$\d+)"/g)].map(m => m[1]))].filter(mp => maps.includes(mp)).sort();
|
|
386
|
+
const hypName = (map) => `h_${map.replace('$', '')}`;
|
|
387
|
+
const arithBinders = (law) => arithIn(law).map(mp => ` (${hypName(mp)} : μ ${q(mp)} = fun x => x ${arithOf(mp).op} ${arithOf(mp).k})`).join('');
|
|
388
|
+
const keyName = (law, name) => { const j = (law.keys ?? []).indexOf(name); return name === law.forall ? 'k' : j >= 0 ? `k${j + 2}` : undefined; };
|
|
389
|
+
const statement = (law, plain = true) => {
|
|
390
|
+
const bound = law.ex?.vars ?? [];
|
|
378
391
|
const t = (e) => {
|
|
379
392
|
switch (e.op) {
|
|
380
393
|
case 'var': {
|
|
381
|
-
const
|
|
382
|
-
return
|
|
394
|
+
const b = bound.indexOf(e.name);
|
|
395
|
+
return b >= 0 ? `w${b + 1}` : keyName(law, e.name) ?? `ρ ${q(e.name)}`;
|
|
383
396
|
}
|
|
384
397
|
case 'const': return String(e.value);
|
|
385
398
|
case 'add': return `(${t(e.a)} + ${t(e.b)})`;
|
|
@@ -389,7 +402,10 @@ export function readable(id, laws, vars, maps, reach) {
|
|
|
389
402
|
case 'min':
|
|
390
403
|
case 'max': return `(${e.op} ${t(e.a)} ${t(e.b)})`;
|
|
391
404
|
case 'if': return `(if ${p(e.c)} then ${t(e.then)} else ${t(e.else)})`;
|
|
392
|
-
case 'get':
|
|
405
|
+
case 'get': {
|
|
406
|
+
const a = plain ? arithOf(e.map) : undefined;
|
|
407
|
+
return a ? `(${t(e.key)} ${a.op} ${a.k})` : `(μ ${q(e.map)} (${t(e.key)}))`;
|
|
408
|
+
}
|
|
393
409
|
}
|
|
394
410
|
};
|
|
395
411
|
const p = (c) => {
|
|
@@ -404,16 +420,170 @@ export function readable(id, laws, vars, maps, reach) {
|
|
|
404
420
|
case 'all': throw new Error(`${id}: a law has no quantifier inside`);
|
|
405
421
|
}
|
|
406
422
|
};
|
|
407
|
-
return law.
|
|
423
|
+
return quantified(law, law.ex ? `∃ ${bound.map((_, i) => `w${i + 1}`).join(' ')} : Nat, ${p(law.ex.body)}` : p(law.holds));
|
|
424
|
+
};
|
|
425
|
+
const haveOf = (j) => (reach.have ? reach.have(j) : `have hl : ${id}_l${j}.Holds ρ μ := ${reach.call} ${id}_e${j} (by simp [${id}_proof])`);
|
|
426
|
+
// The existential of law j (one with `ex`): its theorem over the machine's own variables, then over named ones.
|
|
427
|
+
const someOf = (law, j) => {
|
|
428
|
+
const ex = law.ex, lit = reach.lit, eLit = reach.eLit;
|
|
429
|
+
if (!lit || !eLit)
|
|
430
|
+
throw new Error(`${id}: ${law.name} has "there exists" and no printer for its condition`);
|
|
431
|
+
if (law.keys?.length || (law.forall && ex.ghost))
|
|
432
|
+
throw new Error(`${id}: ${law.name}: "there exists" past one key, or through a ghost under a key, isn't exported`);
|
|
433
|
+
const ix = (name) => { const i = vars.indexOf(name); if (i < 0)
|
|
434
|
+
throw new Error(`${id}: no variable for ${name}`); return i; };
|
|
435
|
+
const ws = ex.vars.map((_, i) => `w${i + 1}`), at = (env, vals) => `updAll ${env} [${ex.vars.map((n, i) => `(${ix(n)}, ${vals[i]})`).join(', ')}]`;
|
|
436
|
+
const P = `(${lit(ex.body)})`, key = law.forall ? ix(law.forall) : undefined;
|
|
437
|
+
const sigma = ex.sub ? `[${ex.vars.map(n => `(${ix(n)}, ${eLit(ex.sub[n])})`).join(', ')}]` : '';
|
|
438
|
+
const machine = ex.ghost ? `${id}_ghosted.orig` : `${id}_machine`;
|
|
439
|
+
const head = `theorem some${j} : ∀ ρ μ, SReach ${machine} ρ μ → ${key !== undefined ? '∀ k : Nat, ' : ''}∃ ${ws.join(' ')} : Nat, evalC (${at(key !== undefined ? `(upd ρ ${key} k)` : 'ρ', ws)}) μ ${P} = true`;
|
|
440
|
+
const proof = ex.ghost
|
|
441
|
+
? ` intro ρ μ h
|
|
442
|
+
obtain ⟨ρg, hg⟩ := some_of_ghost ${id}_ghosted ${id}_ghostOk [${ex.vars.map(ix).join(', ')}] ${P} (by ${checkBy}) (fun ρ μ h => by
|
|
443
|
+
${haveOf(j)}
|
|
444
|
+
exact hl) ρ μ h
|
|
445
|
+
exact ⟨${ex.vars.map(n => `ρg ${ix(n)}`).join(', ')}, hg⟩`
|
|
446
|
+
: ` intro ρ μ h${key !== undefined ? ' k' : ''}
|
|
447
|
+
${haveOf(j)}
|
|
448
|
+
have hs : substC ${sigma} ${P} = ${id}_l${j}.holds := beqC_eq (by ${checkBy})
|
|
449
|
+
have hq : evalC ${key !== undefined ? `(upd ρ ${key} k)` : 'ρ'} μ (substC ${sigma} ${P}) = true := by rw [hs]; exact hl${key !== undefined ? ' k' : ''}
|
|
450
|
+
exact ⟨${ex.vars.map(n => `applyS ${sigma} ${key !== undefined ? `(upd ρ ${key} k)` : 'ρ'} μ ${ix(n)}`).join(', ')}, some_of_subst ${sigma} ${P} _ μ hq⟩`;
|
|
451
|
+
const plain = JSON.stringify(ex.body).includes('"op":"if"') ? `-- ${law.name}: its terms read an if, so no plain statement here (some${j} holds it)` : `/-- ${law.name.replace(/-\//g, '- /')} -/
|
|
452
|
+
theorem law${j} ${reach.binders} (h : SReach ${machine} ρ μ) : ${statement(law)} := by
|
|
453
|
+
${key !== undefined ? 'intro k\n ' : ''}obtain ⟨${ws.join(', ')}, hg⟩ := some${j} ρ μ h${key !== undefined ? ' k' : ''}
|
|
454
|
+
exact ⟨${ws.join(', ')}, (evalC_iff (${at(key !== undefined ? `(upd ρ ${key} k)` : 'ρ', ws)}) μ ${P}).mp hg⟩`;
|
|
455
|
+
return `-- ${law.name}: there exists, proven ${ex.ghost ? 'through ghosts the machine carries' : 'with its witness terms in place'}
|
|
456
|
+
${head} := by
|
|
457
|
+
${proof}
|
|
458
|
+
${plain}`;
|
|
408
459
|
};
|
|
460
|
+
const quantified = (law, body) => (law.keys?.length ? `∀ k ${law.keys.map((_, j) => `k${j + 2}`).join(' ')} : Nat, ${body}` : law.forall ? `∀ k : Nat, ${body}` : body);
|
|
461
|
+
// Where the source says integer (a variable x beside x$neg: the value x − x$neg, core/dsl.ts's Int), the law again
|
|
462
|
+
// over Int: each comparison's two sides moved to one, each integer's two naturals read together as its value, x div k
|
|
463
|
+
// and x mod k of an integer as Lean's Int / and % (pos + (k − 1)·neg's quotient is the value's plus neg, its remainder
|
|
464
|
+
// the value's). Proven from the law over the naturals by omega. Only for laws whose terms are sums (no if, min, max,
|
|
465
|
+
// truncation or product): those have no plain statement over Int to give.
|
|
466
|
+
const ints = vars.filter(x => vars.includes(`${x}$neg`));
|
|
467
|
+
const intStatement = (law) => {
|
|
468
|
+
const plus = (a, b, f = 1) => { const out = new Map([...a].map(([key, v]) => [key, { ...v }])); for (const [key, v] of b) {
|
|
469
|
+
const w = out.get(key);
|
|
470
|
+
if (w)
|
|
471
|
+
w.c += f * v.c;
|
|
472
|
+
else
|
|
473
|
+
out.set(key, { c: f * v.c, show: v.show });
|
|
474
|
+
} return out; };
|
|
475
|
+
const natShow = (e) => { const r = lin(e); if (!r)
|
|
476
|
+
return undefined; return statementOfE(e); };
|
|
477
|
+
const statementOfE = (e) => { const l2 = { ...law, holds: { op: '=', a: e, b: e } }; return statement({ ...l2, forall: undefined, keys: undefined }).split(' = ')[0]; };
|
|
478
|
+
const value = (x) => `((ρ ${q(x)} : Int) - ρ ${q(`${x}$neg`)})`;
|
|
479
|
+
// An integer's pos + (k − 1)·neg, read back as the integer.
|
|
480
|
+
const intKey = (key, k) => {
|
|
481
|
+
const l = lin(key);
|
|
482
|
+
if (!l)
|
|
483
|
+
return undefined;
|
|
484
|
+
const terms = [...l].filter(([, v]) => v.c !== 0);
|
|
485
|
+
const x = ints.find(i => l.get(`v:${i}`)?.c === 1);
|
|
486
|
+
return x && terms.length === (k > 1 ? 2 : 1) && (k === 1 || l.get(`v:${x}$neg`)?.c === k - 1) && !l.has('1') ? x : undefined;
|
|
487
|
+
};
|
|
488
|
+
const lin = (e) => {
|
|
489
|
+
switch (e.op) {
|
|
490
|
+
case 'var': {
|
|
491
|
+
const kn = keyName(law, e.name);
|
|
492
|
+
return new Map([[`v:${e.name}`, { c: 1, show: kn ? `(${kn} : Int)` : `(ρ ${q(e.name)} : Int)` }]]);
|
|
493
|
+
}
|
|
494
|
+
case 'const': return e.value ? new Map([['1', { c: e.value, show: '' }]]) : new Map();
|
|
495
|
+
case 'add': {
|
|
496
|
+
const a = lin(e.a), b = lin(e.b);
|
|
497
|
+
return a && b && plus(a, b);
|
|
498
|
+
}
|
|
499
|
+
case 'mul': {
|
|
500
|
+
const a = lin(e.a);
|
|
501
|
+
return a && new Map([...a].map(([key, v]) => [key, { c: v.c * e.k, show: v.show }]));
|
|
502
|
+
}
|
|
503
|
+
case 'get': {
|
|
504
|
+
const a = arithOf(e.map), x = a ? intKey(e.key, a.k) : undefined;
|
|
505
|
+
if (a && x)
|
|
506
|
+
return a.op === '%' ? new Map([[`mod:${a.k}:${x}`, { c: 1, show: `(${value(x)} % ${a.k})` }]]) : new Map([[`div:${a.k}:${x}`, { c: 1, show: `(${value(x)} / ${a.k})` }], [`v:${x}$neg`, { c: 1, show: `(ρ ${q(`${x}$neg`)} : Int)` }]]);
|
|
507
|
+
const shown = natShow(e.key) === undefined ? undefined : statementOfE(e);
|
|
508
|
+
return shown === undefined ? undefined : new Map([[`get:${shown}`, { c: 1, show: `(${shown} : Int)` }]]);
|
|
509
|
+
}
|
|
510
|
+
case 'sub':
|
|
511
|
+
case 'min':
|
|
512
|
+
case 'max':
|
|
513
|
+
case 'prod':
|
|
514
|
+
case 'if': return undefined;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
const side = (ts) => (ts.length ? ts.map(([show, c]) => (c === 1 ? show : `${c} * ${show}`)).join(' + ') : '0');
|
|
518
|
+
const cmp = (c) => {
|
|
519
|
+
const a = lin(c.a), b = lin(c.b);
|
|
520
|
+
if (!a || !b)
|
|
521
|
+
return undefined;
|
|
522
|
+
const l = plus(a, b, -1);
|
|
523
|
+
// Each integer's two naturals together: c·x − c·x$neg is c times its value.
|
|
524
|
+
for (const x of ints) {
|
|
525
|
+
const pos = l.get(`v:${x}`), neg = l.get(`v:${x}$neg`);
|
|
526
|
+
if (pos && neg && pos.c === -neg.c && pos.c !== 0) {
|
|
527
|
+
l.delete(`v:${x}`);
|
|
528
|
+
l.delete(`v:${x}$neg`);
|
|
529
|
+
l.set(`int:${x}`, { c: pos.c, show: value(x) });
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
const k0 = l.get('1')?.c ?? 0;
|
|
533
|
+
l.delete('1');
|
|
534
|
+
const terms = [...l.values()].filter(v => v.c !== 0);
|
|
535
|
+
const left = [...terms.filter(v => v.c > 0).map(v => [v.show, v.c]), ...(k0 > 0 ? [[String(k0), 1]] : [])];
|
|
536
|
+
const right = [...terms.filter(v => v.c < 0).map(v => [v.show, -v.c]), ...(k0 < 0 ? [[String(-k0), 1]] : [])];
|
|
537
|
+
return `${side(left)} ${c.op === '<=' ? '≤' : c.op} ${side(right)}`;
|
|
538
|
+
};
|
|
539
|
+
const p = (c) => {
|
|
540
|
+
switch (c.op) {
|
|
541
|
+
case '<=':
|
|
542
|
+
case '<':
|
|
543
|
+
case '=': return cmp(c);
|
|
544
|
+
case 'and':
|
|
545
|
+
case 'or': {
|
|
546
|
+
const xs = c.xs.map(p);
|
|
547
|
+
return xs.some(x => x === undefined) ? undefined : xs.length ? `(${xs.join(c.op === 'and' ? ' ∧ ' : ' ∨ ')})` : c.op === 'and' ? 'True' : 'False';
|
|
548
|
+
}
|
|
549
|
+
case 'not': {
|
|
550
|
+
const x = p(c.x);
|
|
551
|
+
return x === undefined ? undefined : `¬(${x})`;
|
|
552
|
+
}
|
|
553
|
+
case 'flag':
|
|
554
|
+
case 'all': return undefined;
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
const reads = new Set([...JSON.stringify(law.holds).matchAll(/"name":"([^"]+)"/g)].map(m => m[1]));
|
|
558
|
+
if (!ints.some(x => reads.has(x) || reads.has(`${x}$neg`)))
|
|
559
|
+
return undefined;
|
|
560
|
+
const body = p(law.holds);
|
|
561
|
+
return body === undefined ? undefined : quantified(law, body);
|
|
562
|
+
};
|
|
563
|
+
const argNames = (binders) => [...binders.matchAll(/\((\S+) :/g)].map(m => m[1]);
|
|
409
564
|
const used = new Set(laws.flatMap(l => [...JSON.stringify(l.holds).matchAll(/"name":"([^"]+)"|"map":"([^"]+)"/g)].map(m => m[1] ?? m[2]).filter(name => name !== l.forall && !(l.keys ?? []).includes(name))));
|
|
410
565
|
return `namespace ${id}
|
|
411
566
|
${[...vars.filter(v => used.has(v)).map(v => `abbrev ${q(v)} : Nat := ${vars.indexOf(v)}`), ...maps.filter(mp => used.has(mp)).map(mp => `abbrev ${q(mp)} : Nat := ${maps.indexOf(mp)}`)].join('\n')}
|
|
412
|
-
${laws.map((law, j) =>
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
567
|
+
${laws.map((law, j) => {
|
|
568
|
+
if (law.ex)
|
|
569
|
+
return someOf(law, j);
|
|
570
|
+
if (JSON.stringify(law.holds).includes('"op":"if"'))
|
|
571
|
+
return `-- ${law.name}: its terms read an if, so no plain statement here (${id}_laws holds it)`;
|
|
572
|
+
const hyps = arithBinders(law), names = arithIn(law).map(hypName), rw = names.length ? `have hμ : ${statement(law, false)} := hl'\n rw [${names.join(', ')}] at hμ\n exact hμ` : `exact hl'`;
|
|
573
|
+
const own = law.keys?.length ? `${reach.have ? reach.have(j) : `have hl : ${id}_e${j}.Holds ρ μ := ${reach.call} ${id}_e${j} (by simp [${id}_proof])`}
|
|
574
|
+
have hl' := fun k ${law.keys.map((_, i) => `k${i + 2}`).join(' ')} => (evalC_iff (upd (${law.keys.reduce((acc, key, i) => `upd (${acc}) ${vars.indexOf(key)} k${i + 2}`, 'ρ')}) ${vars.indexOf(law.forall)} k) μ ${id}_l${j}.holds).mp (hl ${law.keys.map((_, i) => `k${i + 2}`).join(' ')} k)
|
|
575
|
+
${rw}` : `${reach.have ? reach.have(j) : `have hl : ${id}_l${j}.Holds ρ μ := ${reach.call} ${id}_e${j} (by simp [${id}_proof])`}
|
|
576
|
+
${law.forall ? `have hl' := fun k => (evalC_iff (upd ρ ${vars.indexOf(law.forall)} k) μ ${id}_l${j}.holds).mp (hl k)` : `have hl' := (evalC_iff ρ μ ${id}_l${j}.holds).mp hl`}
|
|
577
|
+
${rw}`;
|
|
578
|
+
const int = intStatement(law);
|
|
579
|
+
return (law.stated ? `-- ${law.name}: as written, with some key; proven (below) with the prover's witness in place\n` : '') + `/-- ${law.name.replace(/-\//g, '- /')} -/
|
|
580
|
+
theorem law${j} ${reach.binders}${hyps} (h : ${reach.hyp}) : ${statement(law)} := by
|
|
581
|
+
${own}` + (int === undefined ? '' : `
|
|
582
|
+
/-- ${law.name.replace(/-\//g, '- /')}, over Int -/
|
|
583
|
+
theorem law${j}_int ${reach.binders}${hyps} (h : ${reach.hyp}) : ${int} := by
|
|
584
|
+
have hn := law${j} ${[...argNames(reach.binders), ...names, 'h'].join(' ')}
|
|
585
|
+
${law.forall ? `intro k${(law.keys ?? []).map((_, i) => ` k${i + 2}`).join('')}\n have hn := hn k${(law.keys ?? []).map((_, i) => ` k${i + 2}`).join('')}\n ` : ''}omega`);
|
|
586
|
+
}).join('\n')}
|
|
417
587
|
end ${id}`;
|
|
418
588
|
}
|
|
419
589
|
// A liveness as Live.lean's record: from, to, the rank's parts, each helpful request with its region, the primes.
|
|
@@ -573,7 +743,9 @@ function machineNames(m, laws, allSteps, stepLaws, opts) {
|
|
|
573
743
|
const primes = allSteps.length ? Object.keys(m.state).map(s2 => `${s2}'`) : [];
|
|
574
744
|
const vars = [...new Set([...m.params, ...m.inputs, ...Object.keys(m.state), ...keysOf, ...keysOf.map(k2 => `${k2}#`), '#', ...flags.map(([f]) => `?${f}`),
|
|
575
745
|
...quantified.flatMap(qwits), ...boundKeys(m), ...(opts.tamper ? boundKeys(opts.tamper.machine) : []), ...stepLaws.flatMap(sl => (sl.forall ? [sl.forall] : [])), ...(opts.free ?? []),
|
|
576
|
-
...primes, ...later, ...Array.from({ length: k - 1 }, (_, j) => Object.keys(m.state).map(n2 => stateName(n2, j + 1))).flat(), ...(opts.record ?? []).map(f => `r.${f}`)
|
|
746
|
+
...primes, ...later, ...Array.from({ length: k - 1 }, (_, j) => Object.keys(m.state).map(n2 => stateName(n2, j + 1))).flat(), ...(opts.record ?? []).map(f => `r.${f}`),
|
|
747
|
+
// (the naturals a law with "there exists" binds, which its statement sets: core/exists.ts)
|
|
748
|
+
...laws.flatMap(l => l.ex?.vars ?? [])])];
|
|
577
749
|
const baseMaps = [...new Set([...Object.keys(m.maps ?? {}), ...Object.keys(m.step.maps ?? {}), ...Object.keys(m.fns ?? {})])];
|
|
578
750
|
const maps = [...new Set([...baseMaps, ...carried, ...(allSteps.length ? Object.keys(m.maps ?? {}).map(mp => `${mp}'`) : []), ...laterMaps])];
|
|
579
751
|
return { vars, maps };
|
|
@@ -852,7 +1024,7 @@ def ${id}_proof : List SEntry := ${list(laws.map((_, j) => `${id}_e${j}`))}
|
|
|
852
1024
|
theorem ${id}_check : checkSMachine ${id}_machine ${id}_proof = true := by ${checkBy}
|
|
853
1025
|
theorem ${id}_laws : ∀ ρ M, SReach ${id}_machine ρ M → ∀ e ∈ ${id}_proof, e.Holds ρ M :=
|
|
854
1026
|
smachine_sound ${id}_machine ${id}_proof ${id}_check
|
|
855
|
-
${hm ? histBlock() : readable(id, laws, vars, mapNames, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `SReach ${id}_machine ρ μ`, call: `${id}_laws ρ μ h
|
|
1027
|
+
${hm ? histBlock() : readable(id, laws, vars, mapNames, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `SReach ${id}_machine ρ μ`, call: `${id}_laws ρ μ h`, lit: c, eLit: e })}${opts.noFacts ? '' : factsProof(id, m, mapNames, vars, opts.simp)}${opts.tamper ? `
|
|
856
1028
|
-- TAMPERED: ${opts.tamper.what}. Lean computes the check to false.
|
|
857
1029
|
def ${id}_tampered : SMachine := ${lit(opts.tamper.machine)}
|
|
858
1030
|
example : checkSMachine ${id}_tampered ${id}_proof = false := by ${checkBy}` : ''}${records.length ? `\n\n${records.join('\n\n')}` : ''}${opts.after ? `\n\n${opts.after(ix)}` : ''}`;
|
|
@@ -1157,7 +1329,7 @@ ${cycProofText(id, p, [], vars, maps)}
|
|
|
1157
1329
|
${okLemmas}
|
|
1158
1330
|
${lawBlocks}
|
|
1159
1331
|
${lawsThm}
|
|
1160
|
-
${hm ? histBlock() : readable(id, laws, vars, maps, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `SReach ${id}_machine ρ μ`, call: '',
|
|
1332
|
+
${hm ? histBlock() : readable(id, laws, vars, maps, { binders: '(ρ : Nat → Nat) (μ : Nat → Nat → Nat)', hyp: `SReach ${id}_machine ρ μ`, call: '', lit: c, eLit: e,
|
|
1161
1333
|
have: j => `have hl : holdsOver ${id}_l${j} μ ${keys2(laws[j])} ρ := cyc_machine_holds (${id}_law${j}_holds ρ μ h)` })}${opts.noFacts ? '' : factsProof(id, m, maps, vars, opts.simp)}${records.length ? `\n\n${records.join('\n\n')}` : ''}${opts.after ? `\n\n${opts.after(ix)}` : ''}` };
|
|
1162
1334
|
}
|
|
1163
1335
|
// A fold from its cyclic proof (Bridge.lean's cyc_fold_sound): the fold numbered as foldToLean numbers it, each law's
|
|
@@ -1496,6 +1668,15 @@ function toLeanWith(items, opts) {
|
|
|
1496
1668
|
for (let item of items) {
|
|
1497
1669
|
const id = ident(item.id), tItem = performance.now();
|
|
1498
1670
|
if ('machine' in item) {
|
|
1671
|
+
// Laws with "there exists" go in as the kernel proved them (core/exists.ts): the machine with the ghosts their
|
|
1672
|
+
// witnesses are, each law with its witness terms in place or over its ghosts, each stated again as ∃ (readable).
|
|
1673
|
+
if (item.laws.some(l => l.some?.length)) {
|
|
1674
|
+
const it = item, r = proveStepLaws(it.machine, it.laws, it.stepLaws ?? []);
|
|
1675
|
+
if (!r.proven || !r.laws)
|
|
1676
|
+
throw new Error(`${it.id}: not proven (${r.failed?.stuck ?? ''})`);
|
|
1677
|
+
const ghosts = [...(it.ghosts ?? []), ...Object.keys(r.machine?.state ?? {}).filter(n => !(n in it.machine.state))];
|
|
1678
|
+
item = { ...it, machine: r.machine ?? it.machine, laws: r.laws, ...(ghosts.length ? { ghosts } : {}) };
|
|
1679
|
+
}
|
|
1499
1680
|
const m = item.machine;
|
|
1500
1681
|
// Laws with some key go in as the prover proved them: with its witness in place (the stronger law).
|
|
1501
1682
|
const someKey = (c) => JSON.stringify(c).includes('"op":"all"');
|
|
@@ -1517,7 +1698,7 @@ function toLeanWith(items, opts) {
|
|
|
1517
1698
|
else if (cyc)
|
|
1518
1699
|
certReport.usual.push({ id: item.id, why: cyc.why });
|
|
1519
1700
|
parts.push(`-- ${item.id}\n${defs}${cyc && 'text' in cyc ? cyc.text : smachineToLean(id, m, item.laws, mopts)}`);
|
|
1520
|
-
theorems.push(`${id}_laws`, ...(m.history ? [`${id}_hlaws`] : []), ...(item.stepLaws ?? []).map((_, j) => `${id}_step${j}_holds`), ...(item.liveness ?? []).map((_, j) => `${id}_live${j}_holds`));
|
|
1701
|
+
theorems.push(`${id}_laws`, ...item.laws.flatMap((l, j) => (l.ex ? [`${id}.some${j}`] : [])), ...(m.history ? [`${id}_hlaws`] : []), ...(item.stepLaws ?? []).map((_, j) => `${id}_step${j}_holds`), ...(item.liveness ?? []).map((_, j) => `${id}_live${j}_holds`));
|
|
1521
1702
|
if (item.lean?.after)
|
|
1522
1703
|
theorems.push(...[...item.lean.after(() => 0).matchAll(/^theorem (\S+)/gm)].map(x2 => x2[1]));
|
|
1523
1704
|
}
|
|
@@ -1574,7 +1755,7 @@ function toLeanWith(items, opts) {
|
|
|
1574
1755
|
}
|
|
1575
1756
|
// The file the items need: Live.lean for a liveness or a loop, Sets.lean (which imports Steps.lean) for any machine.
|
|
1576
1757
|
return `import History
|
|
1577
|
-
${cycTexts ? 'import BridgeRules\n' : ''}${items.some(item => 'cyclic' in item) ? (items.some(item => 'cyclic' in item && (item.bridge || item.recur || item.fold)) ? 'import Bridge\n' : 'import Cyclic\n') : ''}${items.some(item => 'cyclic' in item && item.data) ? 'import Data\n' : ''}import ${items.some(item => 'refinement' in item) ? 'Refine' : items.some(item => 'machine' in item && item.ghosts?.length) ? 'Ghost' : items.some(item => ('machine' in item && item.liveness?.length) || 'loop' in item) ? 'Live' : items.some(item => 'machine' in item) ? 'Sets' : 'Steps'}
|
|
1758
|
+
${cycTexts ? 'import BridgeRules\n' : ''}${items.some(item => 'cyclic' in item) ? (items.some(item => 'cyclic' in item && (item.bridge || item.recur || item.fold)) ? 'import Bridge\n' : 'import Cyclic\n') : ''}${items.some(item => 'cyclic' in item && item.data) ? 'import Data\n' : ''}import ${items.some(item => 'refinement' in item) ? 'Refine' : items.some(item => 'machine' in item && (item.ghosts?.length || item.laws.some(l => l.some?.length))) ? 'Ghost' : items.some(item => ('machine' in item && item.liveness?.length) || 'loop' in item) ? 'Live' : items.some(item => 'machine' in item) ? 'Sets' : 'Steps'}
|
|
1578
1759
|
open Kern Cert
|
|
1579
1760
|
set_option maxRecDepth 100000
|
|
1580
1761
|
set_option maxHeartbeats 0
|