Skip to content

feat: support well-founded and state-dependent termination measures in Spec.repeatM - #14507

Merged
sgraf812 merged 11 commits into
masterfrom
sg/vcgen-variant
Aug 8, 2026
Merged

feat: support well-founded and state-dependent termination measures in Spec.repeatM#14507
sgraf812 merged 11 commits into
masterfrom
sg/vcgen-variant

Conversation

@sgraf812

@sgraf812 sgraf812 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

This PR generalizes the termination measures of vcgen's while loop specifications. A measure may map into any type with a WellFoundedRelation instance and may read monadic state:

case inv2 => exact .ofMeasure fun i => i            -- Nat measure
case inv2 => exact .ofMeasure fun (i, j) => (i, j)  -- lexicographic
case inv2 => exact .ofMeasure fun _ s => n - s      -- reads the monadic state

Decrease obligations for Nat-valued measures have the shape f a' < ma and fall to grind; other measure types reduce with RepeatVariant.evalsBelow_ofMeasure to the goals termination_by produces, where decreasing_tactic applies.

A RepeatVariant bundles the type of measure values, its well-founded relation and the evaluation of the measure inside the assertion lattice. The step of Spec.repeatM pins the measure value ma of the current iteration with measure.EvalsTo a ma in its precondition and bounds the value of the next iteration with measure.EvalsBelow a' ma in its postcondition. Simp and grind lemmas collapse EvalsTo and EvalsBelow applications of ofMeasure measures, following the ofProp_apply scheme.

@sgraf812 sgraf812 added the changelog-library Library label Jul 22, 2026
@github-actions github-actions Bot added the toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN label Jul 23, 2026
@mathlib-lean-pr-testing

mathlib-lean-pr-testing Bot commented Jul 23, 2026

Copy link
Copy Markdown

Mathlib CI status (docs):

  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto 3259610687883ec1ea48c481aba2469f2f83facf. You can force Mathlib CI using the force-mathlib-ci label. (2026-07-23 14:50:09)
  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto f2bcf2e8660ab2d16cf3cb50c8e127de0439a337. You can force Mathlib CI using the force-mathlib-ci label. (2026-08-06 10:49:47)
  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto c4e6b62c3d955ef20da94310797072f7c4c5fa2b. You can force Mathlib CI using the force-mathlib-ci label. (2026-08-06 13:06:42)
  • ❗ Batteries/Mathlib CI will not be attempted unless your PR branches off the nightly-with-mathlib branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto bd97ed8e35639e17a11d1490cbc46d994e7b4513. You can force Mathlib CI using the force-mathlib-ci label. (2026-08-07 17:44:33)
  • ✅ Mathlib branch lean-pr-testing-14507 has successfully built against this PR. (2026-08-08 12:42:41) View Log

@leanprover-bot

leanprover-bot commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Reference manual CI status:

  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto 49ff95727f98d43984726b26742d17a1ceea9dd5. You can force reference manual CI using the force-manual-ci label. (2026-07-23 14:50:11)
  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto 23393b959b33e3a8d15796b2397f8a04c315b9f4. You can force reference manual CI using the force-manual-ci label. (2026-08-06 10:49:49)
  • ❗ Reference manual CI will not be attempted unless your PR branches off the nightly-with-manual branch. Try git rebase 749f580c3b7aec0908a057a51a89b304ecbd8151 --onto c4e6b62c3d955ef20da94310797072f7c4c5fa2b. You can force reference manual CI using the force-manual-ci label. (2026-08-07 17:44:35)
  • ❗ Reference manual CI can not be attempted yet, as the nightly-testing-2026-08-08 tag does not exist there yet. We will retry when you push more commits. If you rebase your branch onto nightly-with-manual, reference manual CI should run now. You can force reference manual CI using the force-manual-ci label. (2026-08-08 11:47:50)

@sgraf812 sgraf812 changed the title feat: support state-dependent termination measures in Spec.repeatM feat: support well-founded and state-dependent termination measures in Spec.repeatM Aug 6, 2026
@sgraf812
sgraf812 marked this pull request as ready for review August 7, 2026 17:40
@sgraf812
sgraf812 requested a review from TwoFX as a code owner August 7, 2026 17:40
@sgraf812
sgraf812 added this pull request to the merge queue Aug 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 7, 2026
sgraf812 and others added 10 commits August 7, 2026 18:16
This PR lets `vcgen` loop measures depend on monadic state, matching the strength of legacy mvcgen's `WhileVariant`.

`Assertion.NondetFun` equips an assertion lattice with total nondeterministic functions into an `outParam` type (`Prop ↦ α`, `σ → Pred ↦ σ → Fun`), with covering law `P ⊑ ⨆ a, evalsTo f a ⊓ P`. `Spec.repeatM` quantifies the current measure value via `evalsTo`, so measures such as `σ → Nat` work without baking `PostShape`/`SVal` into the abstract lattice.

Co-authored-by: Cursor <cursoragent@cursor.com>
This PR generalizes the termination measure of `vcgen`'s loop specifications `Spec.repeatM` and `Spec.forIn_loop`: a `RepeatVariant` now bundles a type of measure values together with a well-founded relation and a lattice-embedded evaluation, so loop measures can map into any type with a `WellFoundedRelation` instance (such as lexicographic products) and may read monadic state. Build one from a measure function with `RepeatVariant.ofMeasure`; decrease proof obligations for state-independent `Nat`-valued measures keep their `⌜f a' < ma⌝ ⊓ P` shape and remain `grind`/`finish`-friendly, while other measure types collapse via `RepeatVariant.decreasesTo_ofMeasure` to the goal shape produced by `termination_by`, so `decreasing_tactic` applies.

`NondetFun` now computes the type of pinned measure values as an `outParam` from the assertion lattice and the measure's function type, so elaborating `RepeatVariant.ofMeasure f` determines the value type from `f` alone. The pure instance works in any assertion lattice at low priority, so state-independent measures need no dummy state binder under stateful lattices. The bundled `RepeatVariant` structure keeps the value type and both instances implicit at construction, so the spec theorems carry no instance arguments that rule application would have to synthesize. The step postcondition is stated via `RepeatVariant.decreasesTo`, which pins the next measure value under a join; for state-independent measures the simp/grind lemmas `decreasesTo_ofMeasure` and `decreasesTo_ofMeasure_nat` eliminate the join, so VCs contain no existential. `ofMeasure` is `@[instance_reducible]` with a packed simp/grind API (`γ_ofMeasure`, `eval_ofMeasure`, `rel_ofMeasure`, `rel_ofMeasure_nat`, `NondetFun.evalsTo_pure`): patterns stay valid because attribute-time pattern compilation only unfolds reducible definitions, while simp matching and type class resolution see through the constructor at instances transparency. The proof of `Spec.repeatM` performs `Acc` induction on pinned measure values.
…heory

The join that pins the next measure value now occurs exactly once, inside `RepeatVariant.EvalsTo v a Ψ` ("the measure at cursor `a` evaluates to some value `ma` with `Ψ ma`"); `DecreasesTo` is defined in terms of it and no exposed lemma mentions the join. The theory follows the `ofProp_apply` scheme: general pointwise lemmas (`NondetFun.evalsTo_apply`, `RepeatVariant.evalsTo_ofMeasure_apply`) are `simp`-only because an ematch pattern cannot bind the lattice parameter under the arrow, and fixed-arity `Prop`-grounded specializations (`_apply_1` to `_apply_3`) supply the `grind` rules. With `decreasesTo_def` and the pure collapse `evalsTo_ofMeasure` both `simp` and `grind` lemmas, state-dependent measures now discharge with plain `simp at *` followed by `grind`, and the `σ`-indexed `NondetFun` instance drops to `@[instance_reducible]` like the pure one.
…esis

The step postcondition of `Spec.repeatM` and `Spec.forIn_loop` is now `measure.EvalsBelow a' ma ⊓ inv (.inl a')`: the invariant sits outside the join pinning the next measure value, so the exposed statement mentions no existential and no predicate parameter. Pushing the invariant into the join during the proof uses `Lean.Order.iSup_meet_le`, frame elimination derived from `PreservesSup (meet P)`; the specs take a `[∀ P : Pred, PreservesSup (meet P)]` binder, which instance synthesis discharges for every lattice in the `Prop`-based tower.

`Pred`-valued names are PascalCase uniformly: the class field `NondetFun.EvalsTo`, the structure field `RepeatVariant.EvalsTo` (pins the pre-state measure value) and `RepeatVariant.EvalsBelow` (bounds the post-state value). The collapse theory follows the `ofProp_apply` scheme with fixed-arity `Prop`-grounded `grind` rules up to five state layers (`evalsTo_apply_1` to `_5`, `evalsBelow_ofMeasure_apply_1` to `_5`); the generic `evalsBelow_ofMeasure` collapse stays out of the simp set so that `Nat`-valued measures reach `⌜f a' < ma⌝` via `evalsBelow_ofMeasure_nat` instead of a `WellFoundedRelation.rel` application.
The cursor `α`, the measure value type `γ` and the assertion lattice `Pred` of `RepeatVariant` now live in independent universes, and `NondetFun`'s function type `Fun` is freed from the lattice universe (only the state layers `σ` live there), so measures can map into codomains of any universe such as ordinals. The specs' extra universe parameters are undetermined by their conclusions; rule application leaves them as level metavariables that elaborating the invariant assigns.
The `σ`-indexed `NondetFun` instance and the pointwise lemmas `evalsTo_apply` and `evalsBelow_ofMeasure_apply` now take the state layer `σ` in a universe independent of the inner lattice, matching the generality of the pointwise `CompleteLattice` instance, so towers such as `BigState → Prop` with `BigState : Type 1` support state-dependent measures.
…string

Instances carry instance reducibility by declaration, so the attribute on the two `NondetFun` instances is redundant. The module docstring opens with the concrete `Pred = Nat → Prop` example and states the reason `α` is the `outParam`: elaboration knows `Pred` from the goal and `Fun` from the type of the user-written measure, and instance resolution computes the value type.
The `total` law of `NondetFun` and `RepeatVariant` is now `(⨆ a, EvalsTo f a) = ⊤`. The distributed form `P ⊑ ⨆ a, EvalsTo f a ⊓ P` moves into `le_of_total_le`, which derives it from the law and frame elimination under a `[PreservesSup (meet P)]` hypothesis; `Spec.repeatM` discharges that hypothesis from its frame binder. The instance proofs shrink accordingly.
@sgraf812
sgraf812 added this pull request to the merge queue Aug 7, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 7, 2026
@sgraf812
sgraf812 enabled auto-merge August 8, 2026 10:42
@sgraf812
sgraf812 added this pull request to the merge queue Aug 8, 2026
@github-actions github-actions Bot added the mathlib4-nightly-available A branch for this PR exists at leanprover-community/mathlib4-nightly-testing:lean-pr-testing-NNNN label Aug 8, 2026
Merged via the queue into master with commit f29e9e4 Aug 8, 2026
20 checks passed
@sgraf812
sgraf812 deleted the sg/vcgen-variant branch August 8, 2026 12:28
@mathlib-lean-pr-testing mathlib-lean-pr-testing Bot added the builds-mathlib CI has verified that Mathlib builds against this PR label Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builds-mathlib CI has verified that Mathlib builds against this PR changelog-library Library mathlib4-nightly-available A branch for this PR exists at leanprover-community/mathlib4-nightly-testing:lean-pr-testing-NNNN toolchain-available A toolchain is available for this PR, at leanprover/lean4-pr-releases:pr-release-NNNN

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants