SMT encoder: opt-in define-fun-rec for recursive functions - #1478
kondylidou wants to merge 13 commits into
Conversation
Companion to recursiveFnsAsDefineFunRec. The intended client pattern is a primary pass with the default encoding and then a second pass, with define-fun-rec + fmf-fun, over just the obligations that came back unknown -- the weaker proving mode must not touch the others. Without a filter the second pass re-verifies the whole program. When set, obligations whose label is not listed are skipped: no solver call, no result. Mirrors proceduresToVerify (which filters at the pipeline level; this filters at discharge). Test: DefineFunRecTests gains a fourth eval, obligationsToVerify := [b_is_zero] under define-fun-rec + fmf-fun, pinning exactly that one obligation as fail with a = 73, b = 127. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
| are skipped (no solver call, no result). Lets a client re-query a subset | ||
| of obligations, e.g. those a first pass left `unknown`, under different | ||
| options. -/ | ||
| obligationsToVerify : Option (List String) := none |
There was a problem hiding this comment.
I understand why this is used for the define-fun-rec example, but is the intended use case that a user gets the unknown proof obligations, then calls strata again passing in those obligations by name? This does not seem like an ideal workflow.
There was a problem hiding this comment.
The caller isn't a person, it's a dialect front-end: Boole's verify takes the
labels that came back unknown from the results it already has and re-runs only
those under a model-finding configuration. No label is typed by hand.
The objection still lands, the field is a mechanism with the policy left to
every caller, and a caller that gets it wrong is unsound. If you'd rather Strata
owned the loop, I'm happy to move it upstream; one constraint to know is that our
retry also substitutes a different nat encoding, so it re-verifies a different
program, not the same one under different options.
The axiom form of the nat library is what proves things, but it cannot yield a certified model: cvc5 has nothing to evaluate, so a false obligation comes back `unknown` with a junk candidate rather than a counterexample. The computable form - `pos.toInt` and `pos.fromInt` as `define-fun-rec` - is what cvc5's `fmf-fun` can search, and it finds the constructor term. So `Boole.verify` keeps both: the axiom form proves, and on `unknown` it re-runs just those obligations against the computable form with `fmf-fun`. `fmf-fun` weakens proving, which is why it never touches the primary pass and why only a certified failure may replace an `unknown`. Needs strata-org/Strata#1478 for `recursiveFnsAsDefineFunRec` and `obligationsToVerify`; the Strata pin has to move with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A solver timeout is a property of the budget and the machine, not of the encoding, so pinning one made the suite fail for reasons unrelated to the feature. The measurement is kept as a comment instead. Model values are no longer pinned either. `a` and `b` are forced by the preconditions, but the model also assigns the query's unconstrained variables and nothing obliges a solver to choose the same values twice. The verdicts are what the feature promises, so those are what the tests pin. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-optin # Conflicts: # Strata/Languages/Core/Options.lean
Folds in the branch updates made on GitHub. Content-identical to the local tip: the merged tree matches HEAD exactly, since those commits only merged older main, which this branch already contains. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Recursive functions are encoded as uninterpreted functions with per-constructor
axioms. Good for proving, but the solver cannot evaluate them during model
search, so a false obligation whose counterexample must be computed through
the function returns
unknown.This adds two options, both default off:
define-fun-recand skip the axioms. With cvc5's
fmf-funthe solver then finds concreteconstructor terms.
(no solver call, no result for the others). Lets a client re-query just the
obligations a first pass left
unknown, so the weaker proving mode nevertouches the rest. Mirrors proceduresToVerify, at discharge level.
Follow-up to #1432, which made define-fun-rec the default and was withdrawn.
Here the axiom encoding stays the default. The July comparison ran without
fmf-fun, which is why both encodings looked the same then.Changes
self-call resolves to it instead of declaring an uninterpreted twin.
Test (DefineFunRecTests.lean): binary nat, obligation
toInt(a) = 73 ∧ toInt(a + b) = 200 ⊢ toInt(b) = 0, false with b = 127.
11 obligations: 9 library proof obligations + the false one (assert, ensures).
Row 2: the encoding alone does not regress proving. Row 3: fmf-fun weakens
proving, hence row 4 is the intended client pattern (Strata-Boole side, cvc5
only).
Default behaviour unchanged; full StrataTest passes.
🤖 Generated with Claude Code