Summary
Request: expose SMT-LIB :pattern annotation on user-written \A/\E quantifiers via a TLAPS surface-syntax pragma, so users can guide Z3's MBQI quantifier instantiation on heavily-quantified obligations.
Motivation (empirical, from production HFT verification project)
We maintain ~81 TLA+ specs / 564 invariants / 17 modules, including Raft consensus, matching engine (AMM with curve executors), and lock-tracking proof families. Recent verification campaigns surfaced a recurring SMT cascade pattern on heavily-quantified TypeOK-class obligations:
ChronosRaftProofs.tla TypeOK QED — 9-fact SMT cascade taking 15-30 minutes cold per backend (Z3 / cvc4 / Spass)
- 26→0 axiom-removal campaign — apex obligations (
LeaderElectionLogPrefixOfLog, ClientRequestNoForwardEntry, LogMatchingLocalAxiom) routinely require 6-axis defense-in-depth (TLAPS + Coq + Lean + Why3 + Z3-standalone + Apalache) because Z3's MBQI loops on \A s \in Server: P(s) shapes when Server carries multiple per-element typing facts (currentTerm[s] \in Nat, votedFor[s] \in Server \cup {NULL}, log[s] \in Seq(LogEntry), role[s] \in {Follower, Candidate, Leader}).
Empirically, the structural workarounds we've discovered (per-element typing, drop-SUFFICES-use-ASSUME/PROVE, function-builder per-conjunct case split, lift-typing-facts-as-explicit-sub-step) buy 30-90% wall-clock improvements but require restructuring proofs around what is fundamentally an SMT-instantiation issue. A source-level :pattern hint would let users keep proof structure idiomatic and instead constrain Z3's instantiator directly.
Current state
The internal SMT translator already wires :pattern annotations end-to-end:
src/expr/e_t.ml:496 — pattern_prop : expr list pfuncs, the property attached to expressions
src/expr/e_t.ml:499-518 — add_pats / remove_pats / map_pats / fold_pats operators
src/backend/smt.ml:618-637 — internal add_patterns injects pattern_prop on translator-emitted \A bs : Mem(_, ex) <=> _ shapes for abstracted operators
src/backend/smtlib.ml:236-250 — pp_print_pat emits (! <expr> :pattern (<pat>)) SMT-LIB output
So the SMT-emitter is fully working — but the only writer is the internal add_patterns site, which fires a hard-coded shape on translator-injected operators. Empirically (verified in our .tlacache/<spec>.tlaps/tlapm_*.smt output), :pattern clauses appear ONLY on built-in TLA+ axioms (smt__TLA____SetExtTrigger, smt__TLA____Mem, smt__TLA____FunIsafcn, etc.) — never on user-defined operators (Server, currentTerm, LogEntry).
Pragma-key parser routes only prover / timeout / tactic (src/backend/prep.ml:1117-1181); any other key raises \"Unknown keyword in proof method definition\" (prep.ml:1182). The grammar (src/expr/e_parser.ml:1132-1134 read_method_arg) accepts arbitrary identifier-keyed args and string_or_float_of_expr-typed values.
Proposed surface syntax
Two compatible options, in order of cleanness:
Option A — pragma-attached BY (preferred, smallest delta)
Pattern(p) == TRUE (*{ by (prover:\"smt3\"; pattern:@) }*)
THEOREM TypeOK
PROOF
<1>1. \A s \in Server: currentTerm[s] \in Nat
BY DEF Init, Next, vars, Pattern(<<currentTerm[s]>>)
<1>2. ...
The pattern: key takes a structured-expression argument (currently read_method_arg accepts only Bstring/Bfloat/Bdef); needs grammar widening to accept Btuple/Bexpr of patterns.
Option B — pattern-attached label (alternative)
THEOREM \A s \in Server: P(s) /\ Q(s)
PROOF (*{ pattern: <<P(s)>> }*)
...
Requires a new pragma site distinct from method-args (currently pragma is consumed only on operator-defs and BY).
We'd recommend Option A as a strictly additive parser change (read_method_arg extension) + new pragma key in compute_meth (route pattern: <expr> to attach pattern_prop on the surrounding obligation).
Empirical wallclock targets
Based on our tlapsProveChronosRaft campaign (commit 2080dc3f8 ClientRequestNoForwardEntry, 13456 obligations PROVED, 6-axis defense):
- Z3 MBQI cold path on TypeOK: 15-30min observed; target with explicit
:pattern (<<currentTerm[s]>>): <2min (4-10× speedup, comparable to documented Z3-pattern speedups in IronFleet/Verus).
- Total Raft module wallclock (
tlapsProveChronosRaftProofs): currently ~50min cold; target <15min.
- Apex axiom-removal campaign: 6-axis defense requirement could collapse to TLAPS-alone for ~40% of axioms if MBQI patterns were guidable.
Why this matters for the broader TLA+ community
The IronFleet / Verus / Dafny ecosystems all expose source-level pattern hints precisely because heavily-quantified specifications hit MBQI saturation in practice. TLAPS is currently the only major TLA+/proof-management toolchain that emits SMT-LIB but blocks users from controlling the pattern annotations — even though the internal infrastructure (pattern_prop, pp_print_pat) is fully operational. Adding a source-level frontend would make TLAPS competitive with these systems on quantifier-heavy proofs.
Acceptance criteria suggestion
- New pragma key
pattern: <expr-list> accepted in read_method_arg grammar.
compute_meth (prep.ml) routes the new key to attach Smt.pattern_prop on the obligation's quantifier-bound conclusion (mirroring the internal add_patterns mechanism).
- New stdlib operator in
library/TLAPS.tla:
Pattern(p) == TRUE (*{ by (pattern:@) }*)
- Documentation example in
doc/web/ showing pattern-hint usage on a TypeOK-shaped THEOREM.
- Round-trip test: cited pattern appears in
.tlacache/<spec>.tlaps/tlapm_*.smt :pattern (...) output.
- No regression on existing test corpus (
make test).
We would be happy to contribute a PR if the design direction is acceptable to the maintainers — wanted to validate the approach + namespace choice (pattern: vs trigger: vs qid:) before opening one.
References
src/expr/e_t.ml:496-518 — pattern_prop infrastructure (already exists)
src/backend/smt.ml:618-637 — internal add_patterns site (only current writer)
src/backend/smtlib.ml:236-250 — :pattern SMT-LIB emitter (already wired)
src/expr/e_parser.ml:1128-1154 — pragma grammar (extension target)
src/backend/prep.ml:1105-1186 — pragma key consumer / compute_meth (extension target)
library/TLAPS.tla:39-115 — current pragma surface (one new operator to add)
Empirical context: #261 (LSP proof decomposition, also targets quantifier obligation handling) is adjacent work in the same neighborhood.
Summary
Request: expose SMT-LIB
:patternannotation on user-written\A/\Equantifiers via a TLAPS surface-syntax pragma, so users can guide Z3's MBQI quantifier instantiation on heavily-quantified obligations.Motivation (empirical, from production HFT verification project)
We maintain ~81 TLA+ specs / 564 invariants / 17 modules, including Raft consensus, matching engine (AMM with curve executors), and lock-tracking proof families. Recent verification campaigns surfaced a recurring SMT cascade pattern on heavily-quantified
TypeOK-class obligations:ChronosRaftProofs.tlaTypeOKQED — 9-fact SMT cascade taking 15-30 minutes cold per backend (Z3 / cvc4 / Spass)LeaderElectionLogPrefixOfLog,ClientRequestNoForwardEntry,LogMatchingLocalAxiom) routinely require 6-axis defense-in-depth (TLAPS + Coq + Lean + Why3 + Z3-standalone + Apalache) because Z3's MBQI loops on\A s \in Server: P(s)shapes whenServercarries multiple per-element typing facts (currentTerm[s] \in Nat,votedFor[s] \in Server \cup {NULL},log[s] \in Seq(LogEntry),role[s] \in {Follower, Candidate, Leader}).Empirically, the structural workarounds we've discovered (per-element typing, drop-
SUFFICES-use-ASSUME/PROVE, function-builder per-conjunct case split, lift-typing-facts-as-explicit-sub-step) buy 30-90% wall-clock improvements but require restructuring proofs around what is fundamentally an SMT-instantiation issue. A source-level:patternhint would let users keep proof structure idiomatic and instead constrain Z3's instantiator directly.Current state
The internal SMT translator already wires
:patternannotations end-to-end:src/expr/e_t.ml:496—pattern_prop : expr list pfuncs, the property attached to expressionssrc/expr/e_t.ml:499-518—add_pats/remove_pats/map_pats/fold_patsoperatorssrc/backend/smt.ml:618-637— internaladd_patternsinjectspattern_propon translator-emitted\A bs : Mem(_, ex) <=> _shapes for abstracted operatorssrc/backend/smtlib.ml:236-250—pp_print_patemits(! <expr> :pattern (<pat>))SMT-LIB outputSo the SMT-emitter is fully working — but the only writer is the internal
add_patternssite, which fires a hard-coded shape on translator-injected operators. Empirically (verified in our.tlacache/<spec>.tlaps/tlapm_*.smtoutput),:patternclauses appear ONLY on built-in TLA+ axioms (smt__TLA____SetExtTrigger,smt__TLA____Mem,smt__TLA____FunIsafcn, etc.) — never on user-defined operators (Server,currentTerm,LogEntry).Pragma-key parser routes only
prover/timeout/tactic(src/backend/prep.ml:1117-1181); any other key raises\"Unknown keyword in proof method definition\"(prep.ml:1182). The grammar (src/expr/e_parser.ml:1132-1134read_method_arg) accepts arbitrary identifier-keyed args andstring_or_float_of_expr-typed values.Proposed surface syntax
Two compatible options, in order of cleanness:
Option A — pragma-attached BY (preferred, smallest delta)
The
pattern:key takes a structured-expression argument (currentlyread_method_argaccepts onlyBstring/Bfloat/Bdef); needs grammar widening to acceptBtuple/Bexprof patterns.Option B — pattern-attached label (alternative)
Requires a new pragma site distinct from method-args (currently
pragmais consumed only on operator-defs andBY).We'd recommend Option A as a strictly additive parser change (
read_method_argextension) + new pragma key incompute_meth(routepattern: <expr>to attachpattern_propon the surrounding obligation).Empirical wallclock targets
Based on our
tlapsProveChronosRaftcampaign (commit2080dc3f8ClientRequestNoForwardEntry, 13456 obligations PROVED, 6-axis defense)::pattern (<<currentTerm[s]>>): <2min (4-10× speedup, comparable to documented Z3-pattern speedups in IronFleet/Verus).tlapsProveChronosRaftProofs): currently ~50min cold; target <15min.Why this matters for the broader TLA+ community
The IronFleet / Verus / Dafny ecosystems all expose source-level pattern hints precisely because heavily-quantified specifications hit MBQI saturation in practice. TLAPS is currently the only major TLA+/proof-management toolchain that emits SMT-LIB but blocks users from controlling the pattern annotations — even though the internal infrastructure (
pattern_prop,pp_print_pat) is fully operational. Adding a source-level frontend would make TLAPS competitive with these systems on quantifier-heavy proofs.Acceptance criteria suggestion
pattern: <expr-list>accepted inread_method_arggrammar.compute_meth(prep.ml) routes the new key to attachSmt.pattern_propon the obligation's quantifier-bound conclusion (mirroring the internaladd_patternsmechanism).library/TLAPS.tla:Pattern(p) == TRUE (*{ by (pattern:@) }*)doc/web/showing pattern-hint usage on aTypeOK-shaped THEOREM..tlacache/<spec>.tlaps/tlapm_*.smt:pattern (...)output.make test).We would be happy to contribute a PR if the design direction is acceptable to the maintainers — wanted to validate the approach + namespace choice (
pattern:vstrigger:vsqid:) before opening one.References
src/expr/e_t.ml:496-518—pattern_propinfrastructure (already exists)src/backend/smt.ml:618-637— internaladd_patternssite (only current writer)src/backend/smtlib.ml:236-250—:patternSMT-LIB emitter (already wired)src/expr/e_parser.ml:1128-1154— pragma grammar (extension target)src/backend/prep.ml:1105-1186— pragma key consumer /compute_meth(extension target)library/TLAPS.tla:39-115— current pragma surface (one new operator to add)Empirical context: #261 (LSP proof decomposition, also targets quantifier obligation handling) is adjacent work in the same neighborhood.