Skip to content

fix: make the Banach fixpoint opaque#498

Merged
Kaptch merged 5 commits into
leanprover-community:masterfrom
sgraf812:packed-fixpoint
Jul 7, 2026
Merged

fix: make the Banach fixpoint opaque#498
Kaptch merged 5 commits into
leanprover-community:masterfrom
sgraf812:packed-fixpoint

Conversation

@sgraf812

@sgraf812 sgraf812 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes a class of deterministic timeouts in isDefEq checks that involve fixpoint.

The regression test Iris.Tests.Fixpoint shows the minimal shape: a contraction Phi on the hom space SiProp -n> SiProp whose body applies the recursive call to two different argument transformations, and a unification between (fixpoint Phi.f) P1 and (fixpoint Phi.f) P2 for syntactically different P1, P2 (a failed exact that a subsequent tactic recovers from). When isDefEq can unfold fixpoint into COFE.compl of the approximation chain, the comparison only resolves after reducing both applications to their True-leaf trees; the two recursive-call arguments give every path through the unfolding a distinct argument, so the defeq cache cannot collapse the descent, and at step index 24 the single failed unification exceeds even the default maxHeartbeats budget. The same mechanism measured in a downstream development (a guarded recursive-domain interpreter): Fixpoint.chain iterations x77-295 and tens of thousands of projection unfolds per goal, ending in deterministic timeouts.

The fix follows #497's treatment of Tower.iso: fixpointP f : { x : α // x ≡ f x } packs the fixpoint together with its unfolding equation as a single opaque value, with the chain construction kept in fixpointAux. fixpoint and fixpoint_unfold are its two projections, so both the elaborator and the kernel treat fixpoint as a stuck constant: the failed unification above is detected at the arguments, within a maxHeartbeats 4000 cap that turns any reintroduction of chain descent into a deterministic timeout. fixpoint_ne is reproved from fixpoint_unfold; every other fixpoint lemma (fixpoint_unique, fixpoint_ind, the fixpointAB family) already lived on the API and is untouched. All statements are unchanged.

As in the #497 test, the test file is deliberately not a module (non-module importers ignore module-system annotations such as @[expose], so opaque is what seals fixpoint for them, and the file checks the seal from such an importer; the IrisTest glob builds it), and Phi's proof fields are sorry (silenced): unification only ever touches the data.

Verified in both directions: replacing fixpoint by the chain-based fixpointAux in the regression example reproduces the deterministic timeout at the 4000 cap (and still at 200000); with the opaque packing it passes under a 400 cap.

Checklist

  • My code follows the mathlib naming and code style conventions
  • I have added my name to the authors section of any appropriate files

sgraf812 added 2 commits July 4, 2026 10:53
fixpointP packs fixpoint with its unfolding equation as one opaque
subtype value; fastFixpoint projects out the point. The opaque constant
is stuck for elaborator and kernel isDefEq alike, and fixpoint_unique
transports the fixpoint API (fastFixpoint_unfold, fastFixpoint_eqv,
fastFixpoint_unique, fastFixpoint_ne). Iris.Tests.Fixpoint exercises
the API on a constant contraction over LeibnizO Nat.
fixpoint is now the value projection of the opaque fixpointP, which
packs the fixpoint together with its unfolding equation as a Subtype;
the chain construction lives on in fixpointAux. Both the elaborator and
the kernel treat fixpoint as a stuck constant during definitional-
equality checks, which keeps isDefEq out of the approximation chain.
fixpoint_unfold is the property projection, fixpoint_ne is reproved
from the unfolding equation, and the rest of the fixpoint API is
unchanged.

The regression test Iris.Tests.Fixpoint elaborates a failing-then-
recovering unification between two applications of a hom-space
fixpoint to different arguments under a maxHeartbeats 4000 cap; chain
descent on that example exceeds even the default heartbeat budget.
@sgraf812 sgraf812 changed the title Add an opaque packed Banach fixpoint fix: make the Banach fixpoint opaque Jul 4, 2026
Drop the constant-contraction API examples; the fixpoint API is already
exercised by the rest of the testsuite. Rewrite the header comment to
explain the failure mechanism: the failed exact unifies two applications
of the same fixpoint to different arguments, a stuck constant fails that
check at the arguments, while unfolding into the approximation chain
doubles the reduction tree at every level with distinct arguments on
each path, defeating the defeq cache. The maxHeartbeats 4000 cap makes
any reintroduction of that unfolding a deterministic failure.
@sgraf812
sgraf812 marked this pull request as ready for review July 4, 2026 10:50
@Kaptch
Kaptch self-requested a review July 5, 2026 11:30
@Kaptch
Kaptch merged commit 65e5f6f into leanprover-community:master Jul 7, 2026
5 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