Files
agda-spa/lean/Spa/Lattice.lean
Danila Fedorin ae030386b4 Lean migration: Phases 0-2 (core lattice/chain, fixpoint, transport)
- lean/ lake project pinned to Lean v4.17.0 + mathlib v4.17.0
- Spa.Lattice: fold monotonicity, FixedHeight/BoundedChains (LTSeries-based),
  FiniteHeightLattice, chain-bottom-is-least; the rest of Lattice.agda,
  Chain.agda and Equivalence.agda lift into mathlib (see LEAN_MIGRATION.md)
- Spa.Fixedpoint: gas-based least-fixpoint computation (doStep/fix/aFix)
- Spa.Isomorphism: FixedHeight transport along monotone inverse pairs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 18:36:43 -07:00

139 lines
5.4 KiB
Lean4
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/-
Port of `Lattice.agda`.
Most of the Agda module is *lifted* into mathlib, since we now work with
propositional equality instead of a setoid:
IsSemilattice A _≈_ _⊔_ ↦ SemilatticeSup α
IsLattice A _≈_ _⊔_ _⊓_ ↦ Lattice α
_≼_ (a ⊔ b ≈ b) ↦ a ≤ b (bridge: `sup_eq_right`)
_≺_ ↦ a < b
Monotonic ↦ Monotone
-assoc/-comm/-idemp ↦ sup_assoc/sup_comm/sup_idem
absorb--/absorb--⊔ ↦ sup_inf_self/inf_sup_self
-refl/-trans/-antisym ↦ le_refl/le_trans/le_antisymm
x≼x⊔y ↦ le_sup_left
-Monotonicˡ/ʳ ↦ sup_le_sup_left/sup_le_sup_right
id-Mono/const-Mono ↦ monotone_id/monotone_const
IsDecidable ↦ DecidableEq (kept only where computation needs it)
Chain (Chain.agda) ↦ LTSeries (chains of `<`); concat ↦ RelSeries.smash
ChainMapping.Chain-map ↦ LTSeries.map (Monotone + Injective ⇒ StrictMono)
What remains custom is exactly what mathlib does not have:
* monotonicity of folds over pairwise-related lists (foldr-Mono & friends),
* the fixed-height machinery (Chain.Height ↦ FixedHeight, Bounded),
* the proof that the bottom of the longest chain is a least element (⊥≼).
-/
import Mathlib.Order.Lattice
import Mathlib.Order.RelSeries
namespace Spa
/-! ### Monotonicity helpers (Lattice.agda, `Monotonic₂` and fold lemmas) -/
/-- Agda: `Monotonic₂` (a pair of one-sided monotonicity proofs). -/
def Monotone₂ {α β γ : Type*} [Preorder α] [Preorder β] [Preorder γ]
(f : α β γ) : Prop :=
( b, Monotone fun a => f a b) ( a, Monotone (f a))
section Folds
variable {α β : Type*} [Preorder α] [Preorder β]
/-- Agda: `foldr-Mono`. `Pairwise _≼₁_` becomes `List.Forall₂ (· ≤ ·)`. -/
theorem foldr_mono {l₁ l₂ : List α} (f : α β β) {b₁ b₂ : β}
(hl : List.Forall₂ (· ·) l₁ l₂) (hb : b₁ b₂)
(hf₁ : b, Monotone fun a => f a b) (hf₂ : a, Monotone (f a)) :
l₁.foldr f b₁ l₂.foldr f b₂ := by
induction hl with
| nil => exact hb
| cons hxy _ ih =>
exact le_trans (hf₁ _ hxy) (hf₂ _ ih)
/-- Agda: `foldl-Mono`. -/
theorem foldl_mono {l₁ l₂ : List α} (f : β α β) {b₁ b₂ : β}
(hl : List.Forall₂ (· ·) l₁ l₂) (hb : b₁ b₂)
(hf₁ : a, Monotone fun b => f b a) (hf₂ : b, Monotone (f b)) :
l₁.foldl f b₁ l₂.foldl f b₂ := by
induction hl generalizing b₁ b₂ with
| nil => exact hb
| cons hxy _ ih =>
exact ih (le_trans (hf₁ _ hb) (hf₂ _ hxy))
omit [Preorder α] in
/-- Agda: `foldr-Mono'` (fixed list, varying accumulator). -/
theorem foldr_mono' (l : List α) (f : α β β)
(hf : a, Monotone (f a)) : Monotone fun b => l.foldr f b := by
intro b₁ b₂ hb
induction l with
| nil => exact hb
| cons x xs ih => exact hf x ih
omit [Preorder α] in
/-- Agda: `foldl-Mono'`. -/
theorem foldl_mono' (l : List α) (f : β α β)
(hf : a, Monotone fun b => f b a) : Monotone fun b => l.foldl f b := by
intro b₁ b₂ hb
induction l generalizing b₁ b₂ with
| nil => exact hb
| cons x xs ih => exact ih (hf x hb)
end Folds
/-! ### Fixed height (Chain.agda `Bounded`/`Height`, Lattice.agda `FixedHeight`) -/
/-- Agda: `Chain.Bounded`. Every `<`-chain has length at most `n`. -/
def BoundedChains (α : Type*) [Preorder α] (n : ) : Prop :=
c : LTSeries α, c.length n
/-- Agda: `Chain.Height` (with `FixedHeight h = Height h` from Lattice.agda).
A longest chain runs from `⊥` to `` and has length exactly `height`;
no chain is longer. -/
structure FixedHeight (α : Type*) [Preorder α] (height : ) where
bot : α
top : α
longestChain : LTSeries α
head_longestChain : longestChain.head = bot
last_longestChain : longestChain.last = top
length_longestChain : longestChain.length = height
bounded : BoundedChains α height
/-- Agda: `Chain.Bounded-suc-n` (a bounded order admits no chain one longer). -/
theorem BoundedChains.no_longer {α : Type*} [Preorder α] {n : }
(h : BoundedChains α n) (c : LTSeries α) : c.length n + 1 :=
fun hc => absurd (h c) (by omega)
/-- Agda: `IsFiniteHeightLattice` / `FiniteHeightLattice` (bundled). -/
class FiniteHeightLattice (α : Type*) [Lattice α] where
height :
fixedHeight : FixedHeight α height
namespace FixedHeight
variable {α : Type*} [Lattice α] {h : }
/-- Agda: `Known-⊥`. -/
def KnownBot (fh : FixedHeight α h) : Prop := a : α, fh.bot a
/-- Agda: `Known-`. -/
def KnownTop (fh : FixedHeight α h) : Prop := a : α, a fh.top
/-- Agda: `⊥≼` — the bottom of the longest chain is a least element.
Same proof: if `⊥ ⊓ a ≠ ⊥` then `⊥ ⊓ a < ⊥` prepends to the longest chain,
contradicting boundedness. (The decidability hypothesis of the Agda proof is
not needed classically.) -/
theorem bot_le (fh : FixedHeight α h) : fh.KnownBot := by
intro a
by_cases heq : fh.bot a = fh.bot
· exact inf_eq_left.mp heq
· exfalso
have hlt : fh.bot a < fh.bot :=
lt_of_le_of_ne inf_le_left heq
exact fh.bounded.no_longer
(fh.longestChain.cons (fh.bot a) (fh.head_longestChain hlt))
(by simp [RelSeries.cons, fh.length_longestChain])
end FixedHeight
end Spa