Skip to content

Engine: check a named reading in the kernel, as a table - #191

Merged
deanberris merged 3 commits into
mainfrom
claude/model-check
Nov 3, 2026
Merged

deanberris merged 3 commits into
mainfrom
claude/model-check

Conversation

@deanberris

Copy link
Copy Markdown
Collaborator

The third item of the profiling plan, after #189 and #190.

Why

After round 6, simp was most of the elaboration time in the slowest modules, and most of that simp came from satisfied_by. Measured serially:

  • 46 s of SolaFide.Dispute's elaboration;
  • 24 s of SolaFide.Results'.

Its recipe unfolds the premise list and rewrites the semantics of every formula, which takes about a second for one sola fide case.

What changes

  • Testimony.Logic.ModelCheck. A reading over an enumeration of claims is a table: True or False at each claim. tableOf reads that table off the definition at elaboration time, as a function to Bool. It also builds the proof that the table agrees with the reading, by cases, as a term rather than goal by goal. The kernel then evaluates every premise under the table with decide +kernel.
  • models_iff_holdsB proves that this evaluation agrees with Foundation's semantics. satisfiable_of_table and independent_of_tables build on it.
  • satisfied_by and leaves_open try the table first, and otherwise fall back to the old recipe. Their syntax is unchanged. I built once with the fallback disabled: no satisfied_by or leaves_open in the arguments or the examples falls back. The only fallback is the tactic module's own deliberate example, fun a => a = Pair.q.
  • refute_with is unchanged, on purpose. The refutation check relies on refute_with unfolding every definition through the argument's unfold set. That is how it tells a genuine "not Horn" failure from a definition someone forgot to tag. A kernel check unfolds everything, tagged or not, so moving it would drop that guarantee silently.
  • No new axiom. A first proof used tauto, which put Classical.choice under every model it checked. The generated pages print each theorem's axioms, and caught it. The proof is now constructive, so the theorems rest on propext and Quot.sound as before, and no generated page changes.
  • Docs. The style guide now says to write a reading as a table, with the reason. The tactic docstrings, CLAUDE.md's layout table and ledger round 9 are updated. The citation check's pin moves from 2298 to 2304 definitions, the new module's.

Measured

scripts/bench-ab.sh origin/main, five interleaved rounds:

Benchmark main This PR Change p
satisfied_by: the Pauline case 1.11 s, 8 MiB 45 ms, 4 MiB −96% 0.008
satisfied_by: Trent grants the subjective genitive 1.05 s, 8 MiB 46 ms, 4 MiB −96% 0.008
leaves_open: parity leaves the canon open 204 ms, 4 MiB 15 ms, 2 MiB −93% 0.008

By hand, two runs each, with only the tactic module's table alternative removed for the base:

  • SolaFide.Dispute elaborates in 35.8 s, against 48.4 s;
  • SolaFide.Results in 12.6 s, against 18.6 s.

The axiom audit now covers 9,670 declarations in the arguments, against 12,414. My reading is that the old recipe's simp proofs created auxiliary declarations. I haven't checked that declaration by declaration. Every theorem is still audited.

Round 7 is in #189 and round 8 in #190, so this is round 9. The ledger table and the pin in arguments/TestimonyArguments/Checks.lean will conflict trivially with whichever merges first.

Gates

All run locally on this head and all pass:

  • lake build and lake build TestimonyTools;
  • lake lint and lake exe axiom-audit, in all three packages;
  • kernel replay, in all three packages;
  • bibgen, argtex, statusgen and argdoc with --check, and exampletex --check;
  • testimony_lint.py and mdbook build.

Generated by Claude Code

`satisfied_by` and `leaves_open` proved a model by having `simp` unfold the
premise list and rewrite the semantics of every formula, a second for one
sola fide case. A reading over an enumeration is a table: each claim True or
False. `Testimony.Logic.ModelCheck` reads the table off the definition, as a
function to Bool with the proof by cases that it agrees with the reading, and
the kernel evaluates every premise under it; `models_iff_holdsB` proves the
evaluation the semantics, constructively, so every theorem rests on the
axioms it did. A reading that is not a table falls back to the old recipe;
none in the arguments or the examples does. `refute_with` keeps its recipe:
the refutation check counts on it unfolding every definition through the
argument's unfold set.

Five interleaved rounds against main: the Pauline case's model 1.11 s to
45 ms (-96%, p = 0.008); a granted denial of Trent's 1.05 s to 46 ms (-96%);
the parity reply's independence 204 ms to 15 ms (-93%). By hand, two runs
each: SolaFide.Dispute 48.4 s to 35.8 s, SolaFide.Results 18.6 s to 12.6 s.
Ledger round 9; the style guide says to write a reading as a table; the
citation check's pin moves to 2304 definitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BC49XsbtehCbmJMavF1K5Q
# Conflicts:
#	arguments/TestimonyArguments/Checks.lean
#	docs/src/performance.md
# Conflicts:
#	arguments/TestimonyArguments/Checks.lean
#	arguments/bench/Engine.lean
#	docs/src/performance.md
@deanberris
deanberris marked this pull request as ready for review November 3, 2026 15:27
@deanberris
deanberris merged commit a831d36 into main Nov 3, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants