Lean migration cleanup: collapse FixedHeight struct into FiniteHeightLattice typeclass

The fable-based migration left a two-layer design (a standalone `FixedHeight α h`
struct, height carried as a type index, plus a `FiniteHeightLattice` wrapper).
This collapses it to the single `FiniteHeightLattice` typeclass (height as a
plain field, `⊥`/`⊤` via `extends Bot`/`Top`), and fixes the fallout so the
whole project builds again (`lake build` green).

- Lattice: repair `FixedHeight.bot_le` (compute the `▸` motive via a forward
  `rw`, drop the leftover `fh.length_longestChain`) and the `bot_le` alias.
- Isomorphism: transport rewritten directly onto `FiniteHeightLattice`, taking
  the source as an instance argument.
- Lattice/Prod, AboveBelow: `FixedHeight`-producing def + wrapper instance
  collapsed into one `FiniteHeightLattice` instance. `head`/`last` proofs use
  term-mode `congrArg` to bridge the `Bot`/`Top` defeq through the
  under-construction instance projection (where `rw`+`rfl` cannot).
- Lattice/IterProd: `fixedHeight` recursion now yields a `FiniteHeightLattice`
  (no height index, so the `.cast (by ring)` reassociations vanish);
  `bot_fixedHeight` reprojected onto the def's own `.bot`.
- Lattice/FiniteMap: `fixedHeight`/`bot_contains_bots` go through transport with
  the IterProd instance resolved by typeclass search; `punitFixedHeight`
  replaced by the `PUnit` instance.
- Analysis/Forward/Lattices: `botV` uses `⊥` instead of the deleted
  `FiniteHeightLattice.bot` accessor.
- Analysis/Sign: `num` case used unimported `ring`; the goal is a pure ℕ→ℤ
  cast identity, closed with `norm_cast`. Also fixes the missing `show` in
  `AboveBelow.monotone₂_of_strict` that left un-beta-reduced redexes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 18:33:48 -05:00
parent b16f14fdfd
commit 2ee32580a2
10 changed files with 115 additions and 457 deletions

View File

@@ -1,16 +1,3 @@
/-
Port of `Lattice/Prod.agda`.
The component-wise lattice structure on `α × β` is lifted into mathlib
(`Prod.instLattice`), as is decidability of equality. What remains custom is
the fixed-height content:
unzip ↦ LTSeries.exists_unzip
a,∙-Monotonic/∙,b-Monotonic ↦ Prod.mk_lt_mk_iff_right/left (strict mono of
the two injections, used to map the chains)
fixedHeight (h₁ + h₂) ↦ FixedHeight.prod
isFiniteHeightLattice ↦ instance FiniteHeightLattice (α × β)
-/
import Spa.Lattice
namespace Spa
@@ -19,8 +6,6 @@ section Unzip
variable {α β : Type*} [PartialOrder α] [PartialOrder β]
/-- Agda: `unzip` — a chain in the product splits into chains of the
components whose lengths sum to at least the original length. -/
theorem LTSeries.exists_unzip (c : LTSeries (α × β)) :
(c₁ : LTSeries α) (c₂ : LTSeries β),
c₁.head = c.head.1 c₁.last = c.last.1
@@ -78,35 +63,35 @@ section FixedHeight
variable {α β : Type*} [Lattice α] [Lattice β]
/-- Agda: `Lattice/Prod.agda`'s `fixedHeight` — the product of lattices of
heights `h₁` and `h₂` has height `h₁ + h₂`. The longest chain climbs the first
component (at `⊥₂`), then the second component (at `⊤₁`). -/
def FixedHeight.prod {h₁ h₂ : } (fhA : FixedHeight α h₁) (fhB : FixedHeight β h₂) :
FixedHeight (α × β) (h₁ + h₂) where
bot := (fhA.bot, fhB.bot)
top := (fhA.top, fhB.top)
longestChain :=
RelSeries.smash
(fhA.longestChain.map (fun a => (a, fhB.bot))
(fun _ _ h => Prod.mk_lt_mk_iff_left.mpr h))
(fhB.longestChain.map (fun b => (fhA.top, b))
(fun _ _ h => Prod.mk_lt_mk_iff_right.mpr h))
(by simp [fhA.last_longestChain, fhB.head_longestChain])
head_longestChain := by simp [fhA.head_longestChain]
last_longestChain := by simp [fhB.last_longestChain]
length_longestChain := by
simp [fhA.length_longestChain, fhB.length_longestChain]
bounded := fun c => by
obtain c₁, c₂, -, -, -, -, hlen := LTSeries.exists_unzip c
have h₁ := fhA.bounded c₁
have h₂ := fhB.bounded c₂
omega
/-- Agda: `Lattice/Prod.agda`'s `isFiniteHeightLattice`/`finiteHeightLattice`. -/
instance [IA : FiniteHeightLattice α] [IB : FiniteHeightLattice β] :
instance prod [A : FiniteHeightLattice α] [B : FiniteHeightLattice β] :
FiniteHeightLattice (α × β) where
height := IA.height + IB.height
fixedHeight := IA.fixedHeight.prod IB.fixedHeight
bot := (( : α), ( : β))
top := (( : α), ( : β))
height := A.height + B.height
longest_chain :=
{ series :=
RelSeries.smash
(A.longest_chain.series.map (fun a => (a, ( : β)))
(fun _ _ h => Prod.mk_lt_mk_iff_left.mpr h))
(B.longest_chain.series.map (fun b => (( : α), b))
(fun _ _ h => Prod.mk_lt_mk_iff_right.mpr h))
(by simp [A.longest_chain.last_series, B.longest_chain.head_series])
head_series :=
(RelSeries.head_smash _).trans
((LTSeries.head_map _ _ _).trans
(congrArg (·, ( : β)) A.longest_chain.head_series))
last_series :=
(RelSeries.last_smash _).trans
((LTSeries.last_map _ _ _).trans
(congrArg (( : α), ·) B.longest_chain.last_series))
length_series := by
show A.longest_chain.series.length + B.longest_chain.series.length = _
rw [A.longest_chain.length_series, B.longest_chain.length_series] }
chains_bounded := fun c => by
obtain c₁, c₂, -, -, -, -, hlen := LTSeries.exists_unzip c
have h₁ := A.chains_bounded c₁
have h₂ := B.chains_bounded c₂
omega
end FixedHeight