From 1b045b9df0234f94876438b0a9078755ba0dc0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20G=C3=A1sp=C3=A1r?= <58485900+gasparattila@users.noreply.github.com> Date: Fri, 8 May 2026 18:18:24 +0000 Subject: [PATCH 01/65] feat(Topology/Sets): separation properties of `(Nonempty)Compacts` (#37674) --- Mathlib/Topology/Sets/VietorisTopology.lean | 103 ++++++++++++++++++-- 1 file changed, 97 insertions(+), 6 deletions(-) diff --git a/Mathlib/Topology/Sets/VietorisTopology.lean b/Mathlib/Topology/Sets/VietorisTopology.lean index b865f743cf..5f6df6e1a3 100644 --- a/Mathlib/Topology/Sets/VietorisTopology.lean +++ b/Mathlib/Topology/Sets/VietorisTopology.lean @@ -281,18 +281,40 @@ theorem _root_.IsCompact.powerset_vietoris {K : Set α} (hK : IsCompact K) : instance [CompactSpace α] : CompactSpace (Set α) := ⟨powerset_univ ▸ isCompact_univ.powerset_vietoris⟩ +theorem subset_closure_of_specializes {s t : Set α} (h : s ⤳ t) : t ⊆ closure s := + h.mem_closed isClosed_closure.powerset_vietoris subset_closure + +theorem specializes_iff {s t : Set α} : s ⤳ t ↔ (∀ x ∈ s, ∃ y ∈ t, x ⤳ y) ∧ t ⊆ closure s := by + refine ⟨fun h => ⟨fun x hx => ?_, subset_closure_of_specializes h⟩, fun ⟨hst, hts⟩ => ?_⟩ + · obtain ⟨y, hyt, hxy⟩ := h.mem_closed (s := {u | (u ∩ closure {x}).Nonempty}) + (isClosed_inter_nonempty_of_isClosed isClosed_closure) ⟨x, hx, subset_closure rfl⟩ + exact ⟨y, hyt, specializes_iff_mem_closure.mpr hxy⟩ + · simp_rw [Specializes, nhds_generateFrom, le_iInf₂_iff] + rintro _ ⟨hs, ⟨U, hU, rfl⟩ | ⟨U, hU, rfl⟩⟩ + · refine iInf₂_le U.powerset ⟨fun x hx => ?_, .inl <| mem_image_of_mem _ hU⟩ + obtain ⟨y, hyt, hxy⟩ := hst x hx + exact hxy.mem_open hU <| hs hyt + · obtain ⟨x, hxt, hxU⟩ := hs + obtain ⟨y, hyU, hys⟩ := mem_closure_iff.mp (hts hxt) U hU hxU + exact iInf₂_le {t | (t ∩ U).Nonempty} ⟨⟨y, hys, hyU⟩, .inr <| mem_image_of_mem _ hU⟩ + +theorem specializes_iff_of_t1Space {s t : Set α} [T1Space α] : s ⤳ t ↔ s ⊆ t ∧ t ⊆ closure s := by + simp_rw [specializes_iff, specializes_iff_eq, existsAndEq, and_true, ← subset_def] + +theorem subset_of_specializes {s t : Set α} [T1Space α] (h : s ⤳ t) : s ⊆ t := + (specializes_iff_of_t1Space.mp h).1 + theorem specializes_of_subset_closure {s t : Set α} (hst : s ⊆ t) (hts : t ⊆ closure s) : s ⤳ t := by - simp_rw [Specializes, nhds_generateFrom, le_iInf₂_iff] - rintro _ ⟨hs, ⟨U, hU, rfl⟩ | ⟨U, hU, rfl⟩⟩ - · exact iInf₂_le U.powerset ⟨hst.trans hs, .inl <| mem_image_of_mem _ hU⟩ - · obtain ⟨x, hxt, hxU⟩ := hs - obtain ⟨y, hyU, hys⟩ := mem_closure_iff.mp (hts hxt) U hU hxU - exact iInf₂_le {t | (t ∩ U).Nonempty} ⟨⟨y, hys, hyU⟩, .inr <| mem_image_of_mem _ hU⟩ + aesop (add simp specializes_iff) theorem specializes_closure {s : Set α} : s ⤳ closure s := specializes_of_subset_closure subset_closure .rfl +instance [T1Space α] : T0Space (Set α) where + t0 _ _ h := + subset_antisymm (subset_of_specializes h.specializes) (subset_of_specializes h.specializes') + end vietoris namespace Compacts @@ -456,6 +478,54 @@ instance [DiscreteTopology α] : DiscreteTopology (Compacts α) := by theorem discreteTopology_iff : DiscreteTopology (Compacts α) ↔ DiscreteTopology α := ⟨fun _ => isEmbedding_singleton.discreteTopology, fun _ => inferInstance⟩ +instance [T1Space α] : T0Space (Compacts α) := + isEmbedding_coe.t0Space + +instance [T2Space α] : T2Space (Compacts α) where + t2 K₁ K₂ h := by + wlog h' : ¬(K₁ ≤ K₂) generalizing K₁ K₂ + · grind [Disjoint.symm, le_antisymm] + rw [SetLike.not_le_iff_exists] at h' + obtain ⟨x, hx₁, hx₂⟩ := h' + obtain ⟨U, V, hU, hV, hU', hV', hUV⟩ := K₂.isCompact.separation_of_notMem hx₂ + exact ⟨_, _, isOpen_inter_nonempty_of_isOpen hV, isOpen_subsets_of_isOpen hU, ⟨x, hx₁, hV'⟩, + hU', by grind [Set.Nonempty]⟩ + +@[simp] +theorem t2Space_iff : T2Space (Compacts α) ↔ T2Space α := + ⟨fun _ => isEmbedding_singleton.t2Space, fun _ => inferInstance⟩ + +instance [RegularSpace α] : RegularSpace (Compacts α) := by + simp_rw [regularSpace_generateFrom induced_generateFrom_eq, image_union, image_image, powerset, + preimage_setOf_eq, Filter.disjoint_iff] + rintro _ (⟨U, hU, rfl⟩ | ⟨U, hU, rfl⟩) K hK + · obtain ⟨V, W, hV, hW, hKV, hUW, hVW⟩ := + SeparatedNhds.of_isCompact_isClosed K.isCompact hU.isClosed_compl + (disjoint_compl_right_iff_subset.mpr hK) + refine ⟨{K | (↑K ∩ W).Nonempty}, ?_, {K | ↑K ⊆ V}, + (isOpen_subsets_of_isOpen hV).mem_nhds_iff.mpr hKV, by grind [Set.Nonempty]⟩ + simp_rw [(isOpen_inter_nonempty_of_isOpen hW).mem_nhdsSet, compl_setOf, + ← inter_compl_nonempty_iff] + grw [hUW] + · obtain ⟨x, hx₁, hx₂⟩ := hK + obtain ⟨V, W, hV, hW, hxV, hUW, hVW⟩ := + SeparatedNhds.of_isCompact_isClosed (isCompact_singleton (x := x)) hU.isClosed_compl + (by simpa) + refine ⟨{K | ↑K ⊆ W}, ?_, {K | (↑K ∩ V).Nonempty}, ?_, by grind [Set.Nonempty]⟩ + · simp_rw [(isOpen_subsets_of_isOpen hW).mem_nhdsSet, compl_setOf, not_nonempty_iff_eq_empty, + ← disjoint_iff_inter_eq_empty, ← subset_compl_iff_disjoint_right] + gcongr + · rw [(isOpen_inter_nonempty_of_isOpen hV).mem_nhds_iff] + exact ⟨x, hx₁, hxV <| Set.mem_singleton x⟩ + +@[simp] +theorem regularSpace_iff : RegularSpace (Compacts α) ↔ RegularSpace α := + ⟨fun _ => isEmbedding_singleton.regularSpace, fun _ => inferInstance⟩ + +@[simp] +theorem t3Space_iff : T3Space (Compacts α) ↔ T3Space α := + ⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩ + theorem isCompact_subsets_of_isCompact {K : Set α} (hK : IsCompact K) : IsCompact {L : Compacts α | ↑L ⊆ K} := by rw [isEmbedding_coe.isCompact_iff] @@ -679,6 +749,27 @@ instance [DiscreteTopology α] : DiscreteTopology (NonemptyCompacts α) := theorem discreteTopology_iff : DiscreteTopology (NonemptyCompacts α) ↔ DiscreteTopology α := ⟨fun _ => isEmbedding_singleton.discreteTopology, fun _ => inferInstance⟩ +instance [T1Space α] : T0Space (NonemptyCompacts α) := + isEmbedding_toCompacts.t0Space + +instance [T2Space α] : T2Space (NonemptyCompacts α) := + isEmbedding_toCompacts.t2Space + +@[simp] +theorem t2Space_iff : T2Space (NonemptyCompacts α) ↔ T2Space α := + ⟨fun _ => isEmbedding_singleton.t2Space, fun _ => inferInstance⟩ + +instance [RegularSpace α] : RegularSpace (NonemptyCompacts α) := + isEmbedding_toCompacts.regularSpace + +@[simp] +theorem regularSpace_iff : RegularSpace (NonemptyCompacts α) ↔ RegularSpace α := + ⟨fun _ => isEmbedding_singleton.regularSpace, fun _ => inferInstance⟩ + +@[simp] +theorem t3Space_iff : T3Space (NonemptyCompacts α) ↔ T3Space α := + ⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩ + instance [CompactSpace α] : CompactSpace (NonemptyCompacts α) := isClosedEmbedding_toCompacts.compactSpace From 043e9e04135db0b68a30660692afa6849a93a243 Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Fri, 8 May 2026 23:32:29 +0000 Subject: [PATCH 02/65] =?UTF-8?q?chore(NumberTheory/Dioph):=20reduce=20def?= =?UTF-8?q?eq=20abuse=20of=20`Set=20=CE=B1=20=3D=20=CE=B1=20=E2=86=92=20Pr?= =?UTF-8?q?op`=20(#39099)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mathlib/NumberTheory/Dioph.lean | 39 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Mathlib/NumberTheory/Dioph.lean b/Mathlib/NumberTheory/Dioph.lean index 3e2135a239..bdbe351279 100644 --- a/Mathlib/NumberTheory/Dioph.lean +++ b/Mathlib/NumberTheory/Dioph.lean @@ -243,7 +243,7 @@ end Polynomials /-- A set `S ⊆ ℕ^α` is Diophantine if there exists a polynomial on `α ⊕ β` such that `v ∈ S` iff there exists `t : ℕ^β` with `p (v, t) = 0`. -/ def Dioph {α : Type u} (S : Set (α → ℕ)) : Prop := - ∃ (β : Type u) (p : Poly (α ⊕ β)), ∀ v, S v ↔ ∃ t, p (v ⊗ t) = 0 + ∃ (β : Type u) (p : Poly (α ⊕ β)), ∀ v, v ∈ S ↔ ∃ t, p (v ⊗ t) = 0 namespace Dioph @@ -253,7 +253,7 @@ variable {α β γ : Type u} {S S' : Set (α → ℕ)} theorem ext (d : Dioph S) (H : ∀ v, v ∈ S ↔ v ∈ S') : Dioph S' := by rwa [← Set.ext H] -theorem of_no_dummies (S : Set (α → ℕ)) (p : Poly α) (h : ∀ v, S v ↔ p v = 0) : Dioph S := +theorem of_no_dummies (S : Set (α → ℕ)) (p : Poly α) (h : ∀ v, v ∈ S ↔ p v = 0) : Dioph S := ⟨PEmpty, ⟨p.map inl, fun v => (h v).trans ⟨fun h => ⟨PEmpty.elim, h⟩, fun ⟨_, ht⟩ => ht⟩⟩⟩ theorem inject_dummies_lem (f : β → γ) (g : γ → Option β) (inv : ∀ x, g (f x) = some x) @@ -267,8 +267,8 @@ theorem inject_dummies_lem (f : β → γ) (g : γ → Option β) (inv : ∀ x, exact ⟨t ∘ f, by rwa [this]⟩ theorem inject_dummies (f : β → γ) (g : γ → Option β) (inv : ∀ x, g (f x) = some x) - (p : Poly (α ⊕ β)) (h : ∀ v, S v ↔ ∃ t, p (v ⊗ t) = 0) : - ∃ q : Poly (α ⊕ γ), ∀ v, S v ↔ ∃ t, q (v ⊗ t) = 0 := + (p : Poly (α ⊕ β)) (h : ∀ v, v ∈ S ↔ ∃ t, p (v ⊗ t) = 0) : + ∃ q : Poly (α ⊕ γ), ∀ v, v ∈ S ↔ ∃ t, q (v ⊗ t) = 0 := ⟨p.map (inl ⊗ inr ∘ f), fun v => (h v).trans <| inject_dummies_lem f g inv _ _⟩ variable (β) in @@ -281,7 +281,7 @@ theorem reindex_dioph (f : α → β) : Dioph S → Dioph {v | v ∘ f ∈ S} theorem DiophList.forall (l : List (Set <| α → ℕ)) (d : l.Forall Dioph) : Dioph {v | l.Forall fun S : Set (α → ℕ) => v ∈ S} := by - suffices ∃ (β : _) (pl : List (Poly (α ⊕ β))), ∀ v, List.Forall (fun S : Set _ => S v) l ↔ + suffices ∃ (β : _) (pl : List (Poly (α ⊕ β))), ∀ v, List.Forall (fun S : Set _ => v ∈ S) l ↔ ∃ t, List.Forall (fun p : Poly (α ⊕ β) => p (v ⊗ t) = 0) pl from let ⟨β, pl, h⟩ := this @@ -335,7 +335,7 @@ theorem union : ∀ (_ : Dioph S) (_ : Dioph S'), Dioph (S ∪ S') /-- A partial function is Diophantine if its graph is Diophantine. -/ def DiophPFun (f : (α → ℕ) →. ℕ) : Prop := - Dioph {v : Option α → ℕ | f.graph (v ∘ some, v none)} + Dioph {v : Option α → ℕ | (v ∘ some, v none) ∈ f.graph} /-- A function is Diophantine if its graph is Diophantine. -/ def DiophFn (f : (α → ℕ) → ℕ) : Prop := @@ -419,7 +419,7 @@ open Vector3 open scoped Vector3 theorem diophFn_vec_comp1 {S : Set (Vector3 ℕ (succ n))} (d : Dioph S) {f : Vector3 ℕ n → ℕ} - (df : DiophFn f) : Dioph {v : Vector3 ℕ n | (f v::v) ∈ S} := + (df : DiophFn f) : Dioph {v : Vector3 ℕ n | (f v :: v) ∈ S} := Dioph.ext (diophFn_comp1 (reindex_dioph _ (none :: some) d) df) (fun v => by dsimp -- TODO: `apply iff_of_eq` is required here, even though `congr!` works on iff below. @@ -430,7 +430,7 @@ theorem diophFn_vec_comp1 {S : Set (Vector3 ℕ (succ n))} (d : Dioph S) {f : Ve set_option backward.isDefEq.respectTransparency false in /-- Deleting the first component preserves the Diophantine property. -/ theorem vec_ex1_dioph (n) {S : Set (Vector3 ℕ (succ n))} (d : Dioph S) : - Dioph {v : Fin2 n → ℕ | ∃ x, (x::v) ∈ S} := + Dioph {v : Fin2 n → ℕ | ∃ x, (x :: v) ∈ S} := ext (ex1_dioph <| reindex_dioph _ (none :: some) d) fun v => exists_congr fun x => by dsimp @@ -440,7 +440,7 @@ theorem vec_ex1_dioph (n) {S : Set (Vector3 ℕ (succ n))} (d : Dioph S) : theorem diophFn_vec (f : Vector3 ℕ n → ℕ) : DiophFn f ↔ Dioph {v | f (v ∘ fs) = v fz} := ⟨reindex_dioph _ (fz ::ₒ fs), reindex_dioph _ (none::some)⟩ -theorem diophPFun_vec (f : Vector3 ℕ n →. ℕ) : DiophPFun f ↔ Dioph {v | f.graph (v ∘ fs, v fz)} := +theorem diophPFun_vec (f : Vector3 ℕ n →. ℕ) : DiophPFun f ↔ Dioph {v | (v ∘ fs, v fz) ∈ f.graph} := ⟨reindex_dioph _ (fz ::ₒ fs), reindex_dioph _ (none::some)⟩ theorem diophFn_compn : @@ -466,7 +466,7 @@ theorem diophFn_compn : congr! 1 ext x; obtain _ | _ | _ := x <;> rfl have : Dioph {v | (v ⊗ f v::fun i : Fin2 n => fl i v) ∈ S} := - @diophFn_compn n (fun v => S (v ∘ inl ⊗ f (v ∘ inl) :: v ∘ inr)) this _ dfl + @diophFn_compn n (fun v => (v ∘ inl ⊗ f (v ∘ inl) :: v ∘ inr) ∈ S) this _ dfl ext this fun v => by dsimp congr! 3 with x @@ -511,14 +511,14 @@ section variable {f g : (α → ℕ) → ℕ} (df : DiophFn f) (dg : DiophFn g) include df dg -theorem dioph_comp2 {S : ℕ → ℕ → Prop} (d : Dioph fun v : Vector3 ℕ 2 => S (v &0) (v &1)) : - Dioph fun v => S (f v) (g v) := dioph_comp d [f, g] ⟨df, dg⟩ +theorem dioph_comp2 {S : ℕ → ℕ → Prop} (d : Dioph {v : Vector3 ℕ 2 | S (v &0) (v &1)}) : + Dioph {v | S (f v) (g v)} := dioph_comp d [f, g] ⟨df, dg⟩ theorem diophFn_comp2 {h : ℕ → ℕ → ℕ} (d : DiophFn fun v : Vector3 ℕ 2 => h (v &0) (v &1)) : DiophFn fun v => h (f v) (g v) := diophFn_comp d [f, g] ⟨df, dg⟩ /-- The set of places where two Diophantine functions are equal is Diophantine. -/ -theorem eq_dioph : Dioph fun v => f v = g v := +theorem eq_dioph : Dioph {v | f v = g v} := dioph_comp2 df dg <| of_no_dummies _ (Poly.proj &0 - Poly.proj &1) fun v => by exact Int.ofNat_inj.symm.trans ⟨@sub_eq_zero_of_eq ℤ _ (v &0) (v &1), eq_of_sub_eq_zero⟩ @@ -573,7 +573,7 @@ theorem sub_dioph : DiophFn fun v ↦ f v - g v := scoped infixl:80 " D- " => Dioph.sub_dioph /-- The set of places where one Diophantine function divides another is Diophantine. -/ -theorem dvd_dioph : Dioph fun v => f v ∣ g v := +theorem dvd_dioph : Dioph {v | f v ∣ g v} := dioph_comp ((D∃) 2 <| D&2 D= D&1 D* D&0) [f, g] ⟨df, dg⟩ @[inherit_doc] @@ -581,7 +581,7 @@ scoped infixl:50 " D∣ " => Dioph.dvd_dioph /-- Diophantine functions are closed under the modulo operation. -/ theorem mod_dioph : DiophFn fun v => f v % g v := - have : Dioph fun v : Vector3 ℕ 3 => (v &2 = 0 ∨ v &0 < v &2) ∧ ∃ x : ℕ, v &0 + v &2 * x = v &1 := + have : Dioph {v : Vector3 ℕ 3 | (v &2 = 0 ∨ v &0 < v &2) ∧ ∃ x : ℕ, v &0 + v &2 * x = v &1} := (D&2 D= D.0 D∨ D&0 D< D&2) D∧ (D∃) 3 <| D&1 D+ D&3 D* D&0 D= D&2 diophFn_comp2 df dg <| (diophFn_vec _).2 <| @@ -601,7 +601,7 @@ scoped infixl:80 " D% " => Dioph.mod_dioph /-- The set of places where two Diophantine functions are congruent modulo a third is Diophantine. -/ -theorem modEq_dioph {h : (α → ℕ) → ℕ} (dh : DiophFn h) : Dioph fun v => f v ≡ g v [MOD h v] := +theorem modEq_dioph {h : (α → ℕ) → ℕ} (dh : DiophFn h) : Dioph {v | f v ≡ g v [MOD h v]} := df D% dh D= dg D% dh @[inherit_doc] @@ -610,8 +610,7 @@ scoped notation "D≡ " => Dioph.modEq_dioph /-- Diophantine functions are closed under integer division. -/ theorem div_dioph : DiophFn fun v => f v / g v := have : - Dioph fun v : Vector3 ℕ 3 => - v &2 = 0 ∧ v &0 = 0 ∨ v &0 * v &2 ≤ v &1 ∧ v &1 < (v &0 + 1) * v &2 := + Dioph {v : Vector3 ℕ 3 | v &2 = 0 ∧ v &0 = 0 ∨ v &0 * v &2 ≤ v &1 ∧ v &1 < (v &0 + 1) * v &2} := (D&2 D= D.0 D∧ D&0 D= D.0) D∨ D&0 D* D&2 D≤ D&1 D∧ D&1 D< (D&0 D+ D.1) D* D&2 diophFn_comp2 df dg <| (diophFn_vec _).2 <| @@ -631,7 +630,7 @@ scoped infixl:80 " D/ " => Dioph.div_dioph open Pell theorem pell_dioph : - Dioph fun v : Vector3 ℕ 4 => ∃ h : 1 < v &0, xn h (v &1) = v &2 ∧ yn h (v &1) = v &3 := by + Dioph {v : Vector3 ℕ 4 | ∃ h : 1 < v &0, xn h (v &1) = v &2 ∧ yn h (v &1) = v &3} := by have : Dioph {v : Vector3 ℕ 4 | 1 < v &0 ∧ v &1 ≤ v &3 ∧ (v &2 = 1 ∧ v &3 = 0 ∨ @@ -656,7 +655,7 @@ theorem pell_dioph : exact Dioph.ext this fun v => matiyasevic.symm theorem xn_dioph : DiophPFun fun v : Vector3 ℕ 2 => ⟨1 < v &0, fun h => xn h (v &1)⟩ := - have : Dioph fun v : Vector3 ℕ 3 => ∃ y, ∃ h : 1 < v &1, xn h (v &2) = v &0 ∧ yn h (v &2) = y := + have : Dioph {v : Vector3 ℕ 3 | ∃ y, ∃ h : 1 < v &1, xn h (v &2) = v &0 ∧ yn h (v &2) = y} := let D_pell := pell_dioph.reindex_dioph (Fin2 4) [&2, &3, &1, &0] (D∃) 3 D_pell (diophPFun_vec _).2 <| From 8675f0a8794ea12ab7da312e62daad477d3c9732 Mon Sep 17 00:00:00 2001 From: Christian Merten <136261474+chrisflav@users.noreply.github.com> Date: Sat, 9 May 2026 13:40:12 +0000 Subject: [PATCH 03/65] feat(CategoryTheory): `ObjectProperty.ind P` is stable under products if `P` is (#39114) From Proetale. --- .../CategoryTheory/ObjectProperty/Ind.lean | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Mathlib/CategoryTheory/ObjectProperty/Ind.lean b/Mathlib/CategoryTheory/ObjectProperty/Ind.lean index 15da34ca25..fe2ef48884 100644 --- a/Mathlib/CategoryTheory/ObjectProperty/Ind.lean +++ b/Mathlib/CategoryTheory/ObjectProperty/Ind.lean @@ -7,6 +7,7 @@ module public import Mathlib.CategoryTheory.Presentable.ColimitPresentation public import Mathlib.CategoryTheory.Presentable.Dense +public import Mathlib.CategoryTheory.Limits.FilteredColimitCommutesProduct /-! # Ind and pro-properties @@ -102,4 +103,58 @@ lemma ind_iff_exists (H : P ≤ isFinitelyPresentable.{w} C) (CostructuredArrow.pre incl (isFinitelyPresentable.{w} C).ι X) exact of_essentiallySmall_index ⟨_, _, hc⟩ fun Y ↦ Y.left.2 +section + +variable {D : Type*} [Category D] (P : ObjectProperty D) (F : C ⥤ D) + +lemma ind_inverseImage_le [PreservesFilteredColimitsOfSize.{w, w} F] : + ind.{w} (P.inverseImage F) ≤ (ind.{w} P).inverseImage F := by + intro X ⟨J, _, _, pres, h⟩ + simp only [prop_inverseImage_iff] + use J, inferInstance, inferInstance, pres.map F, h + +lemma ind_inverseImage_eq_of_isEquivalence [P.IsClosedUnderIsomorphisms] [F.IsEquivalence] : + ind.{w} (P.inverseImage F) = (ind.{w} P).inverseImage F := by + refine le_antisymm (ind_inverseImage_le _ _) fun X ⟨J, _, _, pres, h⟩ ↦ ?_ + refine ⟨J, ‹_›, ‹_›, .ofIso (pres.map F.asEquivalence.inverse) ?_, fun j ↦ ?_⟩ + · exact (F.asEquivalence.unitIso.app X).symm + · exact P.prop_of_iso ((F.asEquivalence.counitIso.app _).symm) (h j) + +lemma ind_iff_of_equivalence (e : C ≌ D) [P.IsClosedUnderIsomorphisms] (X : D) : + ind.{w} (P.inverseImage e.functor) (e.inverse.obj X) ↔ ind.{w} P X := by + dsimp only [ObjectProperty.ind] + congr! + refine ⟨fun ⟨pres, h⟩ ↦ ?_, fun ⟨pres, h⟩ ↦ ?_⟩ + · exact ⟨.ofIso (pres.map e.functor) (e.counitIso.app X), fun i ↦ h i⟩ + · exact ⟨pres.map e.inverse, fun i ↦ P.prop_of_iso ((e.counitIso.app _).symm) (h i)⟩ + +end + +section Products + +private lemma ind_pi_of_ind {ι : Type w} [P.IsClosedUnderLimitsOfShape (Discrete ι)] + [HasProductsOfShape ι C] [IsIPCOfShape.{w} ι C] {X : ι → C} (hc : ∀ i, ind.{w} P (X i)) : + ind.{w} P (∏ᶜ X) := by + choose J _ _ pres hpres using hc + obtain ⟨hc⟩ := IsIPCOfShape.nonempty_isColimit fun i ↦ (pres i).isColimit + exact ⟨∀ j, J j, inferInstance, inferInstance, + { diag := _, ι := _, isColimit := hc }, fun i ↦ P.prop_limit _ fun a ↦ hpres a.1 _⟩ + +instance isClosedUnderLimitsOfShape_ind_discrete {ι : Type*} [Small.{w} ι] + [P.IsClosedUnderLimitsOfShape (Discrete ι)] [HasProductsOfShape ι C] [IsIPCOfShape.{w} ι C] : + (ind.{w} P).IsClosedUnderLimitsOfShape (Discrete ι) := by + refine .mk' fun X ⟨Y, h⟩ ↦ ?_ + let e := equivShrink ι + have : HasProductsOfShape (Shrink.{w} ι) C := + hasLimitsOfShape_of_equivalence (Discrete.equivalence e) + have : IsIPCOfShape.{w} (Shrink.{w} ι) C := .of_equiv e + have : P.IsClosedUnderLimitsOfShape (Discrete (Shrink.{w} ι)) := + .of_equivalence (Discrete.equivalence e) + let iso : limit Y ≅ ∏ᶜ fun i ↦ Y.obj ⟨e.symm i⟩ := + (Pi.isoLimit _).symm ≪≫ (Pi.reindex e.symm _).symm + rw [(ind.{w} P).prop_iff_of_iso iso] + exact ind_pi_of_ind fun i ↦ h _ + +end Products + end CategoryTheory.ObjectProperty From 210dc9f2b75fb0bd7718dcfbe71a4cf31b620cb0 Mon Sep 17 00:00:00 2001 From: Jack McKoen <104791831+mckoen@users.noreply.github.com> Date: Sat, 9 May 2026 14:45:26 +0000 Subject: [PATCH 04/65] feat(AlgebraicTopology/Quasicategory): inner fibrations and inner anodyne morphisms (#37869) Defines inner fibrations, inner anodyne morphisms, and immediate results. --- Mathlib.lean | 2 + .../Quasicategory/Basic.lean | 19 ++++ .../Quasicategory/InnerFibration.lean | 99 +++++++++++++++++ .../AnodyneExtensions/Inner/Basic.lean | 100 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 Mathlib/AlgebraicTopology/Quasicategory/InnerFibration.lean create mode 100644 Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/Basic.lean diff --git a/Mathlib.lean b/Mathlib.lean index 82154452e7..1993c03a94 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -1493,6 +1493,7 @@ public import Mathlib.AlgebraicTopology.ModelCategory.RightHomotopy public import Mathlib.AlgebraicTopology.ModelCategory.Transport public import Mathlib.AlgebraicTopology.MooreComplex public import Mathlib.AlgebraicTopology.Quasicategory.Basic +public import Mathlib.AlgebraicTopology.Quasicategory.InnerFibration public import Mathlib.AlgebraicTopology.Quasicategory.Nerve public import Mathlib.AlgebraicTopology.Quasicategory.StrictBicategory public import Mathlib.AlgebraicTopology.Quasicategory.StrictSegal @@ -1524,6 +1525,7 @@ public import Mathlib.AlgebraicTopology.SimplicialObject.II public import Mathlib.AlgebraicTopology.SimplicialObject.Op public import Mathlib.AlgebraicTopology.SimplicialObject.Split public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Basic +public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Inner.Basic public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.IsUniquelyCodimOneFace public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Op public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Pairing diff --git a/Mathlib/AlgebraicTopology/Quasicategory/Basic.lean b/Mathlib/AlgebraicTopology/Quasicategory/Basic.lean index ae3fd9152a..3b21f50c23 100644 --- a/Mathlib/AlgebraicTopology/Quasicategory/Basic.lean +++ b/Mathlib/AlgebraicTopology/Quasicategory/Basic.lean @@ -71,4 +71,23 @@ lemma quasicategory_of_filler (S : SSet) rw [← h j hj, NatTrans.comp_app] rfl +lemma quasicategory_of_hasLiftingProperty (S : SSet) {X : SSet} (t : Limits.IsTerminal X) + (h : ∀ {n : ℕ} {i : Fin (n + 1)} (_ : 0 < i) (_ : i < Fin.last n), + HasLiftingProperty Λ[n, i].ι (t.from S)) : + Quasicategory S where + hornFilling' n i σ₀ h0 hn := + let := h h0 hn + ⟨(CommSq.mk (t.hom_ext (σ₀ ≫ t.from S) (Λ[n + 2, i].ι ≫ t.from Δ[n + 2]))).lift, by simp⟩ + +lemma Quasicategory.hasLiftingProperty (S : SSet) [Quasicategory S] {X : SSet} + (t : Limits.IsTerminal X) {n : ℕ} {i : Fin (n + 1)} (h0 : 0 < i) (hn : i < Fin.last n) : + HasLiftingProperty Λ[n, i].ι (t.from S) where + sq_hasLift _ := + ⟨(hornFilling h0 hn _).choose, (hornFilling h0 hn _).choose_spec.symm, t.hom_ext _ _⟩ + +lemma quasicategory_iff_hasLiftingProperty (S : SSet) {X : SSet} (t : Limits.IsTerminal X) : + Quasicategory S ↔ ∀ {n : ℕ} {i : Fin (n + 1)} (_ : 0 < i) (_ : i < Fin.last n), + HasLiftingProperty Λ[n, i].ι (t.from S) := + ⟨fun _ ↦ Quasicategory.hasLiftingProperty S t, quasicategory_of_hasLiftingProperty S t⟩ + end SSet diff --git a/Mathlib/AlgebraicTopology/Quasicategory/InnerFibration.lean b/Mathlib/AlgebraicTopology/Quasicategory/InnerFibration.lean new file mode 100644 index 0000000000..bea12c034d --- /dev/null +++ b/Mathlib/AlgebraicTopology/Quasicategory/InnerFibration.lean @@ -0,0 +1,99 @@ +/- +Copyright (c) 2026 Jack McKoen. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jack McKoen +-/ +module + +public import Mathlib.AlgebraicTopology.Quasicategory.Basic + +/-! +# Inner fibrations + +Inner fibrations of simplicial sets are the morphisms in `SSet` which have the right lifting +property with respect to all inner horn inclusions. + +Basic consequences of inner fibrations with respect to the definition of quasi-categories are +formalized. + +-/ + +public section + +open CategoryTheory MorphismProperty Simplicial + +universe u + +namespace SSet + +/-- The family of morphisms in `SSet` which consists of inner horn inclusions +`Λ[n, i].ι : Λ[n, i] ⟶ Δ[n]` (for `0 < i < n`). -/ +inductive innerHornInclusions : MorphismProperty SSet.{u} where + | intro {n : ℕ} (i : Fin (n + 3)) (h0 : 0 < i) (hn : i < Fin.last (n + 2)) : + innerHornInclusions Λ[n + 2, i].ι + +lemma horn_ι_mem_innerHornInclusions {n : ℕ} {i : Fin (n + 1)} + (h0 : 0 < i) (hn : i < Fin.last n) : innerHornInclusions (horn.{u} n i).ι := by + obtain _ | _ | k := n + · grind + · grind + · exact ⟨i, h0, hn⟩ + +lemma innerHornInclusions_eq_iSup : + innerHornInclusions.{u} = + ⨆ n, .ofHoms (fun p : {p : Fin (n + 3) // 0 < p ∧ p < Fin.last (n + 2)} ↦ Λ[n + 2, p].ι) := by + ext + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · obtain @⟨n, i, h0, hn⟩ := h + simp only [iSup_iff, ofHoms_iff, Subtype.exists, exists_prop] + use n, i + · simp only [iSup_iff, ofHoms_iff] at h + obtain ⟨n, ⟨i, h0, hn⟩, _, _⟩ := h + exact horn_ι_mem_innerHornInclusions h0 hn + +lemma innerHornInclusions_le_J : innerHornInclusions.{u} ≤ modelCategoryQuillen.J := + fun _ _ _ ⟨_, _, _⟩ ↦ modelCategoryQuillen.horn_ι_mem_J .. + +lemma innerHornInclusions_le_monomorphisms : + innerHornInclusions.{u} ≤ monomorphisms SSet := + innerHornInclusions_le_J.trans modelCategoryQuillen.J_le_monomorphisms + +/-- The inner fibrations are the morphisms which have the right lifting property +with respect to inner horn inclusions. -/ +@[expose, kerodon 01BA] +def innerFibrations : MorphismProperty SSet.{u} := innerHornInclusions.rlp +deriving IsMultiplicative, RespectsIso, IsStableUnderBaseChange, + IsStableUnderRetracts + +/-- A morphism `q` satisfies `[InnerFibration q]` if it belongs to `innerFibrations`. -/ +@[mk_iff] +class InnerFibration {X Y : SSet} (q : X ⟶ Y) : Prop where + mem : innerFibrations q + +lemma mem_innerFibrations {X Y : SSet} (q : X ⟶ Y) [InnerFibration q] : innerFibrations q := + InnerFibration.mem + +lemma quasicategory_of_from_innerFibrations (S : SSet) {X : SSet} (t : Limits.IsTerminal X) + (h : innerFibrations (t.from S)) : Quasicategory S := + quasicategory_of_hasLiftingProperty S t (fun h0 hn ↦ h _ (horn_ι_mem_innerHornInclusions h0 hn)) + +lemma Quasicategory.from_innerFibrations (S : SSet) [Quasicategory S] + {X : SSet} (t : Limits.IsTerminal X) : innerFibrations (t.from S) := + fun _ _ _ ⟨_, h0, hn⟩ ↦ hasLiftingProperty S t h0 hn + +@[kerodon 01BB] +lemma quasicategory_iff_from_innerFibration (S : SSet) {X : SSet} (t : Limits.IsTerminal X) : + Quasicategory S ↔ InnerFibration (t.from S) := + ⟨fun _ ↦ ⟨Quasicategory.from_innerFibrations S t⟩, + fun ⟨h⟩ ↦ quasicategory_of_from_innerFibrations S t h⟩ + +@[kerodon 01BJ] +lemma quasicategory_of_innerFibration_quasicategory {X Y : SSet} {q : X ⟶ Y} [Quasicategory Y] + [InnerFibration q] : Quasicategory X := by + rw [quasicategory_iff_from_innerFibration] + constructor + rw [Limits.terminalIsTerminal.hom_ext (Limits.terminalIsTerminal.from X) + (q ≫ Limits.terminalIsTerminal.from Y)] + exact comp_mem _ _ _ (mem_innerFibrations q) (Quasicategory.from_innerFibrations Y _) + +end SSet diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/Basic.lean b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/Basic.lean new file mode 100644 index 0000000000..3d7a60bba3 --- /dev/null +++ b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/Basic.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Jack McKoen. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jack McKoen +-/ +module + +public import Mathlib.AlgebraicTopology.Quasicategory.InnerFibration +public import Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Basic +public import Mathlib.AlgebraicTopology.SimplicialSet.Presentable +public import Mathlib.CategoryTheory.SmallObject.Basic + +/-! +# Inner anodyne extensions + +Much of this file is mirrored from +`Mathlib.AlgebraicTopology.SimplicialSet.AnodyneExtensions.Basic`. + +*Inner* anodyne extensions form a property of morphisms in the category of simplicial +sets. It contains *inner* horn inclusions and it is closed under coproducts, pushouts, +transfinite compositions and retracts. Equivalently, using the small +object argument, inner anodyne extensions can be defined (and are defined here) +as the class of morphisms that satisfy the left lifting property with respect +to the class of inner fibrations. + +-/ + +public section + +universe u + +open CategoryTheory HomotopicalAlgebra Simplicial + +namespace SSet + +open MorphismProperty + +/-- In the category of simplicial sets, an *inner* anodyne extension is a morphism +that has the left lifting property with respect to *inner* fibrations, where +an inner fibration is a morphism that has the right lifting property with respect +to inner horn inclusions. -/ +@[expose, kerodon 01BR] +def innerAnodyneExtensions : MorphismProperty SSet.{u} := innerFibrations.llp +deriving IsMultiplicative, RespectsIso, IsStableUnderCobaseChange, + IsStableUnderRetracts, IsStableUnderTransfiniteComposition, + IsStableUnderCoproducts + +lemma innerAnodyneExtensions.of_isIso {X Y : SSet.{u}} (f : X ⟶ Y) [IsIso f] : + innerAnodyneExtensions f := + MorphismProperty.of_isIso innerAnodyneExtensions f + +lemma innerAnodyneExtensions_eq_llp_rlp : + innerAnodyneExtensions.{u} = innerHornInclusions.rlp.llp := + rfl + +lemma innerAnodyneExtensions.horn_ι {n : ℕ} {i : Fin (n + 1)} + (h0 : 0 < i) (hn : i < Fin.last n) : + innerAnodyneExtensions.{u} Λ[n, i].ι := by + rw [innerAnodyneExtensions_eq_llp_rlp] + exact le_llp_rlp _ _ (horn_ι_mem_innerHornInclusions h0 hn) + +lemma innerAnodyneExtensions_le : innerAnodyneExtensions ≤ anodyneExtensions.{u} := by + rw [anodyneExtensions_eq_llp_rlp, innerAnodyneExtensions_eq_llp_rlp, le_llp_iff_le_rlp, + rlp_llp_rlp] + exact antitone_rlp innerHornInclusions_le_J + +attribute [local instance] Cardinal.fact_isRegular_aleph0 + Cardinal.orderBotAleph0OrdToType + +instance : MorphismProperty.IsSmall.{u} innerHornInclusions.{u} := by + rw [innerHornInclusions_eq_iSup] + have (n : ℕ) : MorphismProperty.IsSmall.{u} + (MorphismProperty.ofHoms.{u} + fun p : {p : Fin (n + 3) // 0 < p ∧ p < Fin.last (n + 2)} ↦ Λ[n + 2, p].ι) := + isSmall_ofHoms .. + exact isSmall_iSup _ + +instance : IsCardinalForSmallObjectArgument innerHornInclusions.{u} Cardinal.aleph0.{u} where + preservesColimit {A B X Y} i hi f hf := by + have : IsFinitelyPresentable.{u} A := by + simp only [innerHornInclusions_eq_iSup, iSup_iff] at hi + obtain ⟨n, ⟨i⟩⟩ := hi + infer_instance + infer_instance + +instance : HasSmallObjectArgument.{u} innerHornInclusions.{u} where + exists_cardinal := ⟨.aleph0, inferInstance, inferInstance, inferInstance⟩ + +lemma innerAnodyneExtensions_eq_retracts_transfiniteCompositions : + innerAnodyneExtensions = (transfiniteCompositions.{u} + (coproducts.{u} innerHornInclusions.{u}).pushouts).retracts := by + rw [innerAnodyneExtensions_eq_llp_rlp, llp_rlp_of_hasSmallObjectArgument] + +lemma innerAnodyneExtensions_eq_retracts_transfiniteCompositionsOfShape : + innerAnodyneExtensions = (transfiniteCompositionsOfShape + (coproducts.{u} innerHornInclusions.{u}).pushouts ℕ).retracts := by + rw [innerAnodyneExtensions_eq_llp_rlp, + SmallObject.llp_rlp_of_isCardinalForSmallObjectArgument_aleph0] + +end SSet From 14d1d1eb3f51e72099214c005ac63aef27a900e9 Mon Sep 17 00:00:00 2001 From: Yongle Hu Date: Sat, 9 May 2026 15:08:31 +0000 Subject: [PATCH 05/65] chore(RingTheory/Localization/Finiteness): change some `CommRing` to `CommSemiring` (#39112) --- Mathlib/RingTheory/Localization/Finiteness.lean | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Mathlib/RingTheory/Localization/Finiteness.lean b/Mathlib/RingTheory/Localization/Finiteness.lean index 90a08641dc..cb5a31c468 100644 --- a/Mathlib/RingTheory/Localization/Finiteness.lean +++ b/Mathlib/RingTheory/Localization/Finiteness.lean @@ -35,8 +35,8 @@ section open scoped Pointwise -variable {R S : Type*} [CommRing R] [CommRing S] (M : Submonoid R) (f : R →+* S) -variable (R' S' : Type*) [CommRing R'] [CommRing S'] +variable {R S : Type*} [CommSemiring R] [CommSemiring S] (M : Submonoid R) (f : R →+* S) +variable (R' S' : Type*) [CommSemiring R'] [CommSemiring S'] variable [Algebra R R'] [Algebra S S'] open scoped Classical in @@ -146,7 +146,7 @@ lemma of_isLocalization (R S) {Rₚ Sₚ : Type*} [CommSemiring R] [CommSemiring use T.image (algebraMap S Sₚ) simpa using span_eq_top_localization_localization Rₚ M Sₚ hT -instance {R S : Type*} [CommRing R] {P : Ideal R} [CommRing S] [Algebra R S] +instance {R S : Type*} [CommSemiring R] {P : Ideal R} [CommSemiring S] [Algebra R S] [Module.Finite R S] [P.IsPrime] : Module.Finite (Localization.AtPrime P) (Localization (Algebra.algebraMapSubmonoid S P.primeCompl)) := @@ -169,7 +169,7 @@ instance [Module.Finite R M] : Module.Finite (Localization S) (LocalizedModule S end -variable {R : Type u} [CommRing R] {M : Type w} [AddCommMonoid M] [Module R M] +variable {R : Type u} [CommSemiring R] {M : Type w} [AddCommMonoid M] [Module R M] /-- If there exists a finite set `{ r }` of `R` such that `Mᵣ` is `Rᵣ`-finite for each `r`, @@ -182,7 +182,7 @@ See `of_localizationSpan'` for a version without the finite set assumption. -/ theorem of_localizationSpan_finite' (t : Finset R) (ht : Ideal.span (t : Set R) = ⊤) {Mₚ : ∀ (_ : t), Type*} [∀ (g : t), AddCommMonoid (Mₚ g)] [∀ (g : t), Module R (Mₚ g)] - {Rₚ : ∀ (_ : t), Type u} [∀ (g : t), CommRing (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] + {Rₚ : ∀ (_ : t), Type*} [∀ (g : t), CommSemiring (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] [∀ (g : t), IsLocalization.Away g.val (Rₚ g)] [∀ (g : t), Module (Rₚ g) (Mₚ g)] [∀ (g : t), IsScalarTower R (Rₚ g) (Mₚ g)] (f : ∀ (g : t), M →ₗ[R] Mₚ g) [∀ (g : t), IsLocalizedModule (Submonoid.powers g.val) (f g)] @@ -218,7 +218,7 @@ See `Module.Finite.of_localizationSpan_finite` for the specialized version. -/ theorem of_localizationSpan' (t : Set R) (ht : Ideal.span t = ⊤) {Mₚ : ∀ (_ : t), Type*} [∀ (g : t), AddCommMonoid (Mₚ g)] [∀ (g : t), Module R (Mₚ g)] - {Rₚ : ∀ (_ : t), Type u} [∀ (g : t), CommRing (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] + {Rₚ : ∀ (_ : t), Type*} [∀ (g : t), CommSemiring (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] [h₁ : ∀ (g : t), IsLocalization.Away g.val (Rₚ g)] [∀ (g : t), Module (Rₚ g) (Mₚ g)] [∀ (g : t), IsScalarTower R (Rₚ g) (Mₚ g)] (f : ∀ (g : t), M →ₗ[R] Mₚ g) [h₂ : ∀ (g : t), IsLocalizedModule (Submonoid.powers g.val) (f g)] @@ -263,7 +263,7 @@ end Module namespace Ideal -variable {R : Type u} [CommRing R] +variable {R : Type u} [CommSemiring R] /-- If `I` is an ideal such that there exists a set `{ r }` of `R` such that the image of `I` in `Rᵣ` is finitely generated for each `r`, then `I` is finitely @@ -277,7 +277,7 @@ lemma fg_of_localizationSpan {I : Ideal R} (t : Set R) (ht : Ideal.span t = ⊤) end Ideal -variable {R : Type u} [CommRing R] {S : Type v} [CommRing S] {f : R →+* S} +variable {R : Type u} [CommSemiring R] {S : Type v} [CommSemiring S] {f : R →+* S} /-- To check that the kernel of a ring homomorphism is finitely generated, From 1ad783f9bf0bf732fe50c8098b4ecd14ae6bad5b Mon Sep 17 00:00:00 2001 From: Thomas Browning <13339017+tb65536@users.noreply.github.com> Date: Sat, 9 May 2026 15:32:22 +0000 Subject: [PATCH 06/65] feat(FieldTheory/Galois/IsGaloisGroup): the top subgroup of a Galois group is a Galois group (#39093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR proves `IsGaloisGroup (⊤ : Subgroup G) A B ↔ IsGaloisGroup G A B`. Co-authored-by: tb65536 --- Mathlib/FieldTheory/Galois/IsGaloisGroup.lean | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean index f86dbc4416..f75a19b0b7 100644 --- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean +++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean @@ -65,6 +65,21 @@ theorem IsGaloisGroup.of_mulEquiv [hG : IsGaloisGroup G A B] {H : Type*} [Group have he' : ∀ (g : G) (x : B), e.symm g • x = g • x := fun g x ↦ by simp [← he] hG.isInvariant.isInvariant b (fun g ↦ by simpa [he'] using h (e.symm g))⟩ +variable {G A B} in +theorem IsGaloisGroup.iff_of_mulEquiv {H : Type*} [Group H] [MulSemiringAction H B] + (e : H ≃* G) (he : ∀ h (x : B), e h • x = h • x) : + IsGaloisGroup H A B ↔ IsGaloisGroup G A B := by + refine ⟨fun h ↦ h.of_mulEquiv e.symm fun g x ↦ ?_, fun h ↦ h.of_mulEquiv e he⟩ + rw [← he, e.apply_symm_apply] + +variable {G A B} in +@[simp] +theorem IsGaloisGroup.top_iff : IsGaloisGroup (⊤ : Subgroup G) A B ↔ IsGaloisGroup G A B := + iff_of_mulEquiv Subgroup.topEquiv fun _ _ ↦ rfl + +instance [IsGaloisGroup G A B] : IsGaloisGroup (⊤ : Subgroup G) A B := + IsGaloisGroup.top_iff.mpr ‹_› + attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant variable [FaithfulSMul A B] [hA : IsGaloisGroup G A B] From 26c4381a5076743fda14205120db86e9e4d6c01a Mon Sep 17 00:00:00 2001 From: Jovan Gerbscheid <56355248+JovanGerb@users.noreply.github.com> Date: Sat, 9 May 2026 23:47:38 +0000 Subject: [PATCH 07/65] feat(Translate): locally modify name guessing dictionaries (#37808) This PR adds the feature to `to_dual` and `to_additive` that you can now locally modify the name translation dictionary. With this change, we run the risk of having the automated translations being harder to predict. For this reason, the changes to the dictionary do not persisted through imports. Instead, the change lasts until the end of the file (though it ignores sections). See https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Order.20dual.20tactic/near/580834166 --- Mathlib/Order/GaloisConnection/Basic.lean | 2 ++ Mathlib/Order/GaloisConnection/Defs.lean | 2 ++ Mathlib/Order/Interval/Set/Basic.lean | 34 ++++++++++++----------- Mathlib/Order/Interval/Set/Disjoint.lean | 25 +++++++++-------- Mathlib/Tactic/Translate/Core.lean | 16 ++++++----- Mathlib/Tactic/Translate/GuessName.lean | 26 ++++++++++++++++- Mathlib/Tactic/Translate/ToAdditive.lean | 11 +++++++- Mathlib/Tactic/Translate/ToDual.lean | 15 ++++++++-- MathlibTest/ToDual.lean | 19 +++++++++++++ 9 files changed, 111 insertions(+), 39 deletions(-) diff --git a/Mathlib/Order/GaloisConnection/Basic.lean b/Mathlib/Order/GaloisConnection/Basic.lean index 58add68efc..d02d327044 100644 --- a/Mathlib/Order/GaloisConnection/Basic.lean +++ b/Mathlib/Order/GaloisConnection/Basic.lean @@ -57,6 +57,8 @@ variable [Preorder α] [Preorder β] {l : α → β} {u : β → α} variable (gc : GaloisConnection l u) include gc +to_dual_name_hint U L + @[to_dual] theorem upperBounds_l_image (s : Set α) : upperBounds (l '' s) = u ⁻¹' upperBounds s := diff --git a/Mathlib/Order/GaloisConnection/Defs.lean b/Mathlib/Order/GaloisConnection/Defs.lean index 66ff7d7d02..41dc65626f 100644 --- a/Mathlib/Order/GaloisConnection/Defs.lean +++ b/Mathlib/Order/GaloisConnection/Defs.lean @@ -44,6 +44,8 @@ def GaloisConnection [Preorder α] [Preorder β] (l : α → β) (u : β → α) to_dual_insert_cast GaloisConnection := by rw [forall_comm]; simp only [Iff.comm] +to_dual_name_hint U L + namespace GaloisConnection section diff --git a/Mathlib/Order/Interval/Set/Basic.lean b/Mathlib/Order/Interval/Set/Basic.lean index 4e55aaea10..98c6874023 100644 --- a/Mathlib/Order/Interval/Set/Basic.lean +++ b/Mathlib/Order/Interval/Set/Basic.lean @@ -285,7 +285,9 @@ theorem Iio_ssubset_Iic_self : Iio a ⊂ Iic a := theorem Ioo_subset_Ioo (ha : a₂ ≤ a₁) (hb : b₁ ≤ b₂) : Ioo a₁ b₁ ⊆ Ioo a₂ b₂ := fun _ ⟨hx₁, hx₂⟩ => ⟨ha.trans_lt hx₁, hx₂.trans_le hb⟩ -@[to_dual Ioo_subset_Ioo_right] +to_dual_name_hint Left Right + +@[to_dual] theorem Ioo_subset_Ioo_left (h : a₁ ≤ a₂) : Ioo a₂ b ⊆ Ioo a₁ b := Ioo_subset_Ioo h le_rfl @@ -293,11 +295,11 @@ theorem Ioo_subset_Ioo_left (h : a₁ ≤ a₂) : Ioo a₂ b ⊆ Ioo a₁ b := theorem Ico_subset_Ico (ha : a₂ ≤ a₁) (hb : b₁ ≤ b₂) : Ico a₁ b₁ ⊆ Ico a₂ b₂ := fun _ hx => ⟨ha.trans hx.1, hx.2.trans_le hb⟩ -@[to_dual Ioc_subset_Ioc_right] +@[to_dual] theorem Ico_subset_Ico_left (h : a₁ ≤ a₂) : Ico a₂ b ⊆ Ico a₁ b := Ico_subset_Ico h le_rfl -@[to_dual Ico_subset_Ico_right] +@[to_dual] theorem Ioc_subset_Ioc_left (h : a₁ ≤ a₂) : Ioc a₂ b ⊆ Ioc a₁ b := Ioc_subset_Ioc h le_rfl @@ -305,11 +307,11 @@ theorem Ioc_subset_Ioc_left (h : a₁ ≤ a₂) : Ioc a₂ b ⊆ Ioc a₁ b := theorem Icc_subset_Icc (ha : a₂ ≤ a₁) (hb : b₁ ≤ b₂) : Icc a₁ b₁ ⊆ Icc a₂ b₂ := fun _ ⟨hx₁, hx₂⟩ => ⟨ha.trans hx₁, le_trans hx₂ hb⟩ -@[to_dual Icc_subset_Icc_right] +@[to_dual] theorem Icc_subset_Icc_left (h : a₁ ≤ a₂) : Icc a₂ b ⊆ Icc a₁ b := Icc_subset_Icc h le_rfl -@[to_dual (reorder := ha hb) Icc_ssubset_Icc_right] +@[to_dual (reorder := ha hb)] theorem Icc_ssubset_Icc_left (h₂ : a₂ ≤ b₂) (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Icc a₁ b₁ ⊂ Icc a₂ b₂ := (ssubset_iff_of_subset (Icc_subset_Icc (le_of_lt ha) hb)).mpr ⟨a₂, left_mem_Icc.mpr h₂, not_and.mpr fun f _ => lt_irrefl a₂ (ha.trans_le f)⟩ @@ -318,7 +320,7 @@ theorem Icc_ssubset_Icc_left (h₂ : a₂ ≤ b₂) (ha : a₂ < a₁) (hb : b theorem Ico_subset_Ioo (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Ico a₁ b₁ ⊆ Ioo a₂ b₂ := fun _ hx ↦ ⟨ha.trans_le hx.1, hx.2.trans_le hb⟩ -@[to_dual Ioc_subset_Ioo_right] +@[to_dual] theorem Ico_subset_Ioo_left (h : a₁ < a₂) : Ico a₂ b ⊆ Ioo a₁ b := Ico_subset_Ioo h le_rfl @@ -326,7 +328,7 @@ theorem Ico_subset_Ioo_left (h : a₁ < a₂) : Ico a₂ b ⊆ Ioo a₁ b := theorem Icc_subset_Ioc (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Icc a₁ b₁ ⊆ Ioc a₂ b₂ := fun _ hx ↦ ⟨ha.trans_le hx.1, hx.2.trans hb⟩ -@[to_dual Icc_subset_Ico_right] +@[to_dual] theorem Icc_subset_Ioc_left (h : a₁ < a₂) : Icc a₂ b ⊆ Ioc a₁ b := Icc_subset_Ioc h le_rfl @@ -494,11 +496,11 @@ lemma subsingleton_Icc_iff {α : Type*} [LinearOrder α] {a b : α} : contrapose! h exact ⟨a, ⟨le_refl _, h.le⟩, b, ⟨h.le, le_refl _⟩, h.ne⟩ -@[to_dual (attr := simp) Icc_diff_right] +@[to_dual (attr := simp)] theorem Icc_diff_left : Icc a b \ {a} = Ioc a b := ext fun x => by simp [lt_iff_le_and_ne, eq_comm, and_right_comm] -@[to_dual (attr := simp) Ioc_diff_right] +@[to_dual (attr := simp)] theorem Ico_diff_left : Ico a b \ {a} = Ioo a b := ext fun x => by simp [and_right_comm, ← lt_iff_le_and_ne, eq_comm] @@ -506,7 +508,7 @@ theorem Ico_diff_left : Ico a b \ {a} = Ioo a b := theorem Icc_diff_both : Icc a b \ {a, b} = Ioo a b := by rw [insert_eq, ← diff_diff, Icc_diff_left, Ioc_diff_right] -@[to_dual (attr := simp) Ici_diff_left] +@[to_dual (attr := simp)] theorem Iic_diff_right : Iic a \ {a} = Iio a := ext fun x => by simp [lt_iff_le_and_ne] @@ -527,11 +529,11 @@ theorem Icc_diff_Ioo_same (h : a ≤ b) : Icc a b \ Ioo a b = {a, b} := by theorem Iic_diff_Iio_same : Iic a \ Iio a = {a} := by rw [← Iic_diff_right, diff_diff_cancel_left (singleton_subset_iff.2 self_mem_Iic)] -@[to_dual Ioi_union_left] +@[to_dual] theorem Iio_union_right : Iio a ∪ {a} = Iic a := ext fun _ => le_iff_lt_or_eq.symm -@[to_dual Ioo_union_right] +@[to_dual] theorem Ioo_union_left (hab : a < b) : Ioo a b ∪ {a} = Ico a b := by rw [← Ico_diff_left, diff_union_self, union_eq_self_of_subset_right (singleton_subset_iff.2 <| left_mem_Ico.2 hab)] @@ -543,16 +545,16 @@ theorem Ioo_union_both (h : a ≤ b) : Ioo a b ∪ {a, b} = Icc a b := by | x, .inr rfl => right_mem_Icc.mpr h rw [← this, Icc_diff_both] -@[to_dual Ico_union_right] +@[to_dual] theorem Ioc_union_left (hab : a ≤ b) : Ioc a b ∪ {a} = Icc a b := by rw [← Icc_diff_left, diff_union_self, union_eq_self_of_subset_right (singleton_subset_iff.2 <| left_mem_Icc.2 hab)] -@[to_dual (attr := simp) Ioc_insert_left] +@[to_dual (attr := simp)] theorem Ico_insert_right (h : a ≤ b) : insert b (Ico a b) = Icc a b := by rw [insert_eq, union_comm, Ico_union_right h] -@[to_dual (attr := simp) Ioo_insert_right] +@[to_dual (attr := simp)] theorem Ioo_insert_left (h : a < b) : insert a (Ioo a b) = Ico a b := by rw [insert_eq, union_comm, Ioo_union_left h] @@ -588,7 +590,7 @@ theorem mem_Icc_Ico_Ioc_Ioo_of_subset_of_subset {s : Set α} (ho : Ioo a b ⊆ s rw [← Ico_diff_left, ← Icc_diff_right] apply_rules [subset_diff_singleton] -@[to_dual eq_right_or_mem_Ioo_of_mem_Ioc] +@[to_dual] theorem eq_left_or_mem_Ioo_of_mem_Ico {x : α} (hmem : x ∈ Ico a b) : x = a ∨ x ∈ Ioo a b := hmem.1.eq_or_lt'.imp_right fun h => ⟨h, hmem.2⟩ diff --git a/Mathlib/Order/Interval/Set/Disjoint.lean b/Mathlib/Order/Interval/Set/Disjoint.lean index 04675dbb38..e8640977be 100644 --- a/Mathlib/Order/Interval/Set/Disjoint.lean +++ b/Mathlib/Order/Interval/Set/Disjoint.lean @@ -36,11 +36,14 @@ section Preorder variable [Preorder α] {a b c : α} -@[to_dual (attr := simp) Ici_disjoint_Iio] +to_dual_name_hint Disjoint Disjoint +to_dual_name_hint Left Right + +@[to_dual (attr := simp)] theorem Iic_disjoint_Ioi (h : a ≤ b) : Disjoint (Iic a) (Ioi b) := disjoint_left.mpr fun _ ha hb => (h.trans_lt hb).not_ge ha -@[to_dual (attr := simp) Ioi_disjoint_Iic] +@[to_dual (attr := simp)] theorem Iio_disjoint_Ici (h : a ≤ b) : Disjoint (Iio a) (Ici b) := disjoint_left.mpr fun _ ha hb => (h.trans_lt' ha).not_ge hb @@ -56,7 +59,7 @@ theorem Ioc_disjoint_Ioc_of_le {d : α} (h : b ≤ c) : Disjoint (Ioc a b) (Ioc theorem Ico_disjoint_Ico_same : Disjoint (Ico a b) (Ico b c) := disjoint_left.mpr fun _ hab hbc => hab.2.not_ge hbc.1 -@[to_dual (attr := simp) Iic_disjoint_Ici] +@[to_dual (attr := simp)] theorem Ici_disjoint_Iic : Disjoint (Ici a) (Iic b) ↔ ¬a ≤ b := by rw [Set.disjoint_iff_inter_eq_empty, Ici_inter_Iic, Icc_eq_empty_iff] @@ -67,19 +70,19 @@ theorem Ioc_disjoint_Ioi (h : b ≤ c) : Disjoint (Ioc a b) (Ioi c) := theorem Ioc_disjoint_Ioi_same : Disjoint (Ioc a b) (Ioi b) := Ioc_disjoint_Ioi le_rfl -@[to_dual Iio_disjoint_Ioi_of_not_lt] +@[to_dual] theorem Ioi_disjoint_Iio_of_not_lt (h : ¬a < b) : Disjoint (Ioi a) (Iio b) := disjoint_left.mpr fun _ hx hy ↦ h (hx.trans hy) -@[to_dual Iio_disjoint_Ioi_of_le] +@[to_dual] theorem Ioi_disjoint_Iio_of_le (h : a ≤ b) : Disjoint (Ioi b) (Iio a) := Ioi_disjoint_Iio_of_not_lt (not_lt_of_ge h) -@[to_dual Iio_disjoint_Ioi_same] +@[to_dual] theorem Ioi_disjoint_Iio_same : Disjoint (Ioi a) (Iio a) := Ioi_disjoint_Iio_of_le le_rfl -@[to_dual (attr := simp) Iio_disjoint_Ioi_iff] +@[to_dual (attr := simp)] theorem Ioi_disjoint_Iio_iff [DenselyOrdered α] : Disjoint (Ioi a) (Iio b) ↔ ¬a < b := ⟨fun h hab ↦ (exists_between hab).elim fun _ hc ↦ h.notMem_of_mem_left hc.left hc.right, @@ -89,11 +92,11 @@ theorem Ioi_disjoint_Iio_iff [DenselyOrdered α] : Disjoint (Ioi a) (Iio b) ↔ theorem iUnion_Iic : ⋃ a : α, Iic a = univ := iUnion_eq_univ_iff.2 fun x => ⟨x, self_mem_Iic⟩ -@[to_dual (attr := simp) iUnion_Icc_left] +@[to_dual (attr := simp)] theorem iUnion_Icc_right (a : α) : ⋃ b, Icc a b = Ici a := by simp only [← Ici_inter_Iic, ← inter_iUnion, iUnion_Iic, inter_univ] -@[to_dual (attr := simp) iUnion_Ico_left] +@[to_dual (attr := simp)] theorem iUnion_Ioc_right (a : α) : ⋃ b, Ioc a b = Ioi a := by simp only [← Ioi_inter_Iic, ← inter_iUnion, iUnion_Iic, inter_univ] @@ -101,11 +104,11 @@ theorem iUnion_Ioc_right (a : α) : ⋃ b, Ioc a b = Ioi a := by theorem iUnion_Iio [NoMaxOrder α] : ⋃ a : α, Iio a = univ := iUnion_eq_univ_iff.2 exists_gt -@[to_dual (attr := simp) iUnion_Ioc_left] +@[to_dual (attr := simp)] theorem iUnion_Ico_right [NoMaxOrder α] (a : α) : ⋃ b, Ico a b = Ici a := by simp only [← Ici_inter_Iio, ← inter_iUnion, iUnion_Iio, inter_univ] -@[to_dual (attr := simp) iUnion_Ioo_left] +@[to_dual (attr := simp)] theorem iUnion_Ioo_right [NoMaxOrder α] (a : α) : ⋃ b, Ioo a b = Ioi a := by simp only [← Ioi_inter_Iio, ← inter_iUnion, iUnion_Iio, inter_univ] diff --git a/Mathlib/Tactic/Translate/Core.lean b/Mathlib/Tactic/Translate/Core.lean index 5857d6b791..36f8e71626 100644 --- a/Mathlib/Tactic/Translate/Core.lean +++ b/Mathlib/Tactic/Translate/Core.lean @@ -224,9 +224,9 @@ structure TranslateData : Type where changeNumeral : Bool /-- When `isDual := true`, every translation `A → B` will also give a translation `B → A`. -/ isDual : Bool - guessNameData : GuessName.GuessNameData + guessNameExt : GuessName.GuessNameExt -attribute [inherit_doc GuessName.GuessNameData] TranslateData.guessNameData +attribute [inherit_doc GuessName.GuessNameExt] TranslateData.guessNameExt /-- Get the translation for the given name. -/ def findTranslation? (env : Environment) (t : TranslateData) : Name → Option TranslationInfo := @@ -557,15 +557,16 @@ where return tmpLCtx.mkLambda (usedLetOnly := false) fvars e /-- Rename binder names in pi type. -/ -def renameBinderNames (t : TranslateData) (rename : NameMap Name) (src : Expr) : Expr := +def renameBinderNames (data : GuessName.GuessNameData) (rename : NameMap Name) + (src : Expr) : Expr := src.mapForallBinderNames fun n => (rename.get? n).getD <| match n with | .str p s => .str p <| - let s' := GuessName.guessName t.guessNameData s + let s' := GuessName.guessName data s if s' != s then s' else -- If the name starts with `h`, translate the rest of the name, e.g. `hmax` ↦ `hmin`. if let some suffix := s.dropPrefix? 'h' then - "h" ++ GuessName.guessName t.guessNameData suffix.toString + "h" ++ GuessName.guessName data suffix.toString else s | n => n @@ -633,7 +634,8 @@ def updateDecl (t : TranslateData) (tgt : Name) (srcDecl : ConstantInfo) let mut type := decl.type if let some b := unfoldBoundaries? then type ← b.insertBoundaries decl.type t.attrName - let (type', relevantArg₂) ← applyReplacementForall t dont <| renameBinderNames t rename type + let (type', relevantArg₂) ← applyReplacementForall t dont <| + renameBinderNames (t.guessNameExt.getState (← getEnv)) rename type type ← reorderForall reorder.reorder type' if let some b := unfoldBoundaries? then type ← b.unfoldInsertions type @@ -911,7 +913,7 @@ def targetName (t : TranslateData) (cfg : Config) (src : Name) : CoreM Name := d return tgt let .str pre s := src | throwError "{t.attrName}: can't transport {src}" trace[translate_detail] "The name {s} splits as {open GuessName in s.splitCase}" - let tgt_auto := GuessName.guessName t.guessNameData s + let tgt_auto := GuessName.guessName (t.guessNameExt.getState (← getEnv)) s let depth := cfg.tgt.getNumParts let pre := translateNamespace (← getEnv) pre let (pre1, pre2) := pre.splitAt (depth - 1) diff --git a/Mathlib/Tactic/Translate/GuessName.lean b/Mathlib/Tactic/Translate/GuessName.lean index f3b8355f8c..5f466e2572 100644 --- a/Mathlib/Tactic/Translate/GuessName.lean +++ b/Mathlib/Tactic/Translate/GuessName.lean @@ -15,7 +15,7 @@ public import Mathlib.Init public meta section -open Std +open Lean Std namespace Mathlib.Tactic.GuessName open GuessName -- currently needed to enable projection notation @@ -40,6 +40,7 @@ structure GuessNameData where When applying the dictionary, we lower-case the output if the input was also given in lower-case. -/ abbreviationDict : Std.HashMap String String + deriving Inhabited /-- A set of strings of names that end in a capital letter. * If the string contains a lowercase letter, the string should be split between the first occurrence @@ -176,4 +177,27 @@ def guessName (g : GuessNameData) : String → String := applyNameDict g <| s.splitCase +/-- Environment extension used for guessing the translation of a name. -/ +abbrev GuessNameExt := EnvExtension GuessNameData + +/-- Register a new `GuessNameExt`. -/ +def registerGuessNameExt (data : GuessNameData) : IO GuessNameExt := do + registerEnvExtension (pure data) + +/-- Add the translation `src ↦ tgt` to the `GuessNameExt`. +Both `src` and `tgt` should be capitalized. +This change persists until the end of the current file, but it does not persist through imports. -/ +def GuessNameExt.addTranslation (ext : GuessNameExt) (srcId tgtId : Ident) : + Elab.Command.CommandElabM Unit := do + let src := srcId.getId.toString + let tgt := tgtId.getId.toString + unless src.front.isUpper do throwErrorAt srcId "`{src}` should be capitalized" + unless tgt.front.isUpper do throwErrorAt tgtId "`{tgt}` should be capitalized" + modifyEnv fun env ↦ ext.modifyState env fun data ↦ + let src := src.decapitalizeSeq + if src.splitCase matches [_] then + { data with nameDict := data.nameDict.insert src tgt.splitCase } + else + { data with abbreviationDict := data.abbreviationDict.insert src tgt } + end Mathlib.Tactic.GuessName diff --git a/Mathlib/Tactic/Translate/ToAdditive.lean b/Mathlib/Tactic/Translate/ToAdditive.lean index 4c5243baec..ce6000d5cc 100644 --- a/Mathlib/Tactic/Translate/ToAdditive.lean +++ b/Mathlib/Tactic/Translate/ToAdditive.lean @@ -387,13 +387,17 @@ def abbreviationDict : Std.HashMap String String := .ofList [ ("modObj", "AddModObj"), ("yonedaMon", "yonedaAddMon")] +@[inherit_doc GuessName.GuessNameExt] +initialize guessNameExt : GuessName.GuessNameExt ← + GuessName.registerGuessNameExt { nameDict, abbreviationDict } + /-- The bundle of environment extensions for `to_additive` -/ def data : TranslateData where ignoreArgsAttr; doTranslateAttr; translations attrName := `to_additive changeNumeral := true isDual := false - guessNameData := { nameDict, abbreviationDict } + guessNameExt initialize registerBuiltinAttribute { name := `to_additive @@ -411,4 +415,9 @@ have a corresponding translated declaration. -/ elab "insert_to_additive_translation" src:ident tgt:ident : command => do translations.add src.getId { translation := tgt.getId } +/-- `to_additive_name_hint src tgt` lets `to_additive` translate the name segment `src` to `tgt` +for the rest of the file current. `src` and `tgt` should both be capitalized. -/ +elab "to_additive_name_hint" src:ident tgt:ident : command => do + guessNameExt.addTranslation src tgt + end Mathlib.Tactic.ToAdditive diff --git a/Mathlib/Tactic/Translate/ToDual.lean b/Mathlib/Tactic/Translate/ToDual.lean index 223911f2cd..cca3403ebd 100644 --- a/Mathlib/Tactic/Translate/ToDual.lean +++ b/Mathlib/Tactic/Translate/ToDual.lean @@ -181,8 +181,6 @@ def nameDict : Std.HashMap String (List String) := .ofList [ ("iic", ["Ici"]), ("ioc", ["Ico"]), ("ico", ["Ioc"]), - ("u", ["L"]), - ("l", ["U"]), ("next", ["Prev"]), ("prev", ["Next"]), ("heyting", ["Coheyting"]), @@ -256,6 +254,11 @@ def abbreviationDict : Std.HashMap String String := .ofList [ ("decidableSucc", "DecidablePred"), ] +@[inherit_doc GuessName.GuessNameExt] +initialize guessNameExt : GuessName.GuessNameExt ← + GuessName.registerGuessNameExt { nameDict, abbreviationDict } + + /-- The bundle of environment extensions for `to_dual` -/ def data : TranslateData where ignoreArgsAttr; doTranslateAttr; translations @@ -263,7 +266,7 @@ def data : TranslateData where attrName := `to_dual changeNumeral := false isDual := true - guessNameData := { nameDict, abbreviationDict } + guessNameExt /-- The `to_dual_insert_cast` attribute is used to tag declarations `foo` that should not be unfolded in a proof that is translated. Instead, a rewrite with an equality theorem is inserted. @@ -286,4 +289,10 @@ initialize registerBuiltinAttribute { applicationTime := .afterCompilation } +/-- `to_dual_name_hint src tgt` lets `to_dual` translate between the name segments `src` and `tgt` +for the rest of the file current. `src` and `tgt` should both be capitalized. -/ +elab "to_dual_name_hint" src:ident tgt:ident : command => do + guessNameExt.addTranslation src tgt + guessNameExt.addTranslation tgt src + end Mathlib.Tactic.ToDual diff --git a/MathlibTest/ToDual.lean b/MathlibTest/ToDual.lean index b22bc4eba8..64d779c246 100644 --- a/MathlibTest/ToDual.lean +++ b/MathlibTest/ToDual.lean @@ -418,3 +418,22 @@ class Category.{v,u} (c : Type u) where @[to_dual self (reorder := A B, 2 4)] structure Comma {A : Type u} [Category.{v} A] {B : Type u'} [Category.{v'} B] where + +open Mathlib.Tactic Translate ToDual + +/-- info: "leftMono" -/ +#guard_msgs in +#eval return GuessName.guessName (data.guessNameExt.getState (← getEnv)) "leftMono" + +to_dual_name_hint LeftMono FooBar + +/-- info: "fooBar" -/ +#guard_msgs in +#eval return GuessName.guessName (data.guessNameExt.getState (← getEnv)) "leftMono" + +to_dual_name_hint Left Right +to_dual_name_hint Epi Mono + +/-- info: "right_epi" -/ +#guard_msgs in +#eval return GuessName.guessName (data.guessNameExt.getState (← getEnv)) "left_mono" From 81cccd582d87409c19ce5df079413a747d1950d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Sk=C5=99ivan?= <6596305+lecopivo@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:41 +0000 Subject: [PATCH 08/65] fix(FunProp): be less strict about the shape of morphism theorems (#37441) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't be so restrictive about the shape of morphism theorems Right now, `fun_prop` has a problem with a bundled morphism `Foo α` that coerces to `α → α → α` . The coerced function has two arguments and there is an unnecessary restriction about this. This PR lifts that restriction. [Zulip discussion](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/.60fun_prop.60.20in.20.60FunLike.60.20with.20multiple.20arguments/with/582731349) Co-authored-by: Tomas Skrivan Co-authored-by: Jon Eugster --- Mathlib/Tactic/FunProp/Core.lean | 21 +++++++++--------- Mathlib/Tactic/FunProp/Theorems.lean | 4 ++-- MathlibTest/fun_prop_dev.lean | 33 ++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/Mathlib/Tactic/FunProp/Core.lean b/Mathlib/Tactic/FunProp/Core.lean index 53d2456624..d49e135fa7 100644 --- a/Mathlib/Tactic/FunProp/Core.lean +++ b/Mathlib/Tactic/FunProp/Core.lean @@ -322,6 +322,17 @@ def applyMorRules (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData) (funProp : Expr → FunPropM (Option Result)) : FunPropM (Option Result) := do trace[Debug.Meta.Tactic.fun_prop] "applying morphism theorems to {← ppExpr e}" + -- get theorems + let candidates ← getMorphismTheorems e + trace[Meta.Tactic.fun_prop] + "candidate morphism theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}" + + -- try theorems + for c in candidates do + if let some r ← tryTheorem? e (.decl c.thmName) funProp then + return r + + -- if all failed try to add/remove arguments match ← fData.isMorApplication with | .none => throwError "fun_prop bug: invalid use of mor rules on {← ppExpr e}" | .underApplied => @@ -330,16 +341,6 @@ def applyMorRules (funPropDecl : FunPropDecl) (e : Expr) (fData : FunctionData) let some (f, g) ← fData.peeloffArgDecomposition | return none applyCompRule funPropDecl e f g funProp | .exact => - - let candidates ← getMorphismTheorems e - - trace[Meta.Tactic.fun_prop] - "candidate morphism theorems: {← candidates.mapM fun c => ppOrigin (.decl c.thmName)}" - - for c in candidates do - if let some r ← tryTheorem? e (.decl c.thmName) funProp then - return r - trace[Debug.Meta.Tactic.fun_prop] "no theorem matched" return none diff --git a/Mathlib/Tactic/FunProp/Theorems.lean b/Mathlib/Tactic/FunProp/Theorems.lean index ae0b183c48..a6dc7dcff9 100644 --- a/Mathlib/Tactic/FunProp/Theorems.lean +++ b/Mathlib/Tactic/FunProp/Theorems.lean @@ -358,8 +358,8 @@ def getTheoremFromConst (declName : Name) (prio : Nat := eval_prio default) : Me } -- todo: maybe do a little bit more careful detection of morphism and transition theorems match (← fData.isMorApplication) with - | .exact => return .mor thm - | .underApplied | .overApplied => + | .exact | .overApplied => return .mor thm + | .underApplied => throwError "fun_prop theorem about morphism coercion has to be in fully applied form" | .none => if fData.fn.isFVar && (fData.args.size == 1) && diff --git a/MathlibTest/fun_prop_dev.lean b/MathlibTest/fun_prop_dev.lean index 0b2d0e95fa..a2c6252f06 100644 --- a/MathlibTest/fun_prop_dev.lean +++ b/MathlibTest/fun_prop_dev.lean @@ -699,3 +699,36 @@ example (hX : ∀ i, Lin' (X i)) : Lin (fun ω i ↦ X i ω) := by -- failed in https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Weird.20behavior.20of.20fun_prop end MVarBug + +section BundledMorphismWithFunctionValues + +structure FooHom (α : Type*) where + toFun : α → α → α + cont' : Con (Function.uncurry toFun) + +instance : FunLike (FooHom α) α (α → α) where + coe f := f.toFun + coe_injective' f g h := by cases f; cases g; congr + +@[fun_prop] +theorem con_foohom' {β : Type*} {f : β → FooHom α} (hf : Con f) {g₁ : β → α} (hg₁ : Con g₁) + {g₂ : β → α} (hg₂ : Con g₂) : Con fun x ↦ (f x) (g₁ x) (g₂ x) := silentSorry + +example {f : FooHom α} : Con fun x => f x x := by fun_prop + +example {f : FooHom α} (y : α) : Con fun x => f x y := by fun_prop + +example {f : FooHom α} : Con fun x => f (f x x) x := by fun_prop + +example {f : FooHom (Fin 2 → α)} : Con fun x i => f (f (fun _ => x 0) x) x i := by fun_prop + +example {f : FooHom (Fin 2 → α)} (i) : Con fun x => f (f (fun _ => x 0) x) x i := by fun_prop + +example {f : α → FooHom α} (hf : Con (fun x : α × α × α => f x.1 x.2.1 x.2.2)) : + Con fun x ↦ f x x x := by + fun_prop + +example {f : α → FooHom α} (hf : Con f) : Con fun x ↦ f x (f x x x) x := by + fun_prop + +end BundledMorphismWithFunctionValues From 175f378ee555dc64b1ae158cfc41dcf6601aaa3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 00:45:43 +0000 Subject: [PATCH 09/65] chore: split `SetTheory.Cardinal.Cofinality` (#38363) We split this file into a `Basic` file with only the basic results on `Order.cof`, and an `Ordinal` file for the interactions with ordinals. This avoids an import cycle in a subsequent PR. The module docstrings were rewritten, but no theorems were changed. --- Mathlib.lean | 3 +- .../Presentable/IsCardinalFiltered.lean | 2 +- Mathlib/LinearAlgebra/Dimension/Finite.lean | 2 +- Mathlib/Order/Filter/Cocardinal.lean | 2 +- .../SetTheory/Cardinal/Cofinality/Basic.lean | 203 ++++++++++++++++++ .../Ordinal.lean} | 201 +---------------- Mathlib/SetTheory/Cardinal/Regular.lean | 2 +- .../Ordinal/FundamentalSequence.lean | 2 +- 8 files changed, 219 insertions(+), 198 deletions(-) create mode 100644 Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean rename Mathlib/SetTheory/Cardinal/{Cofinality.lean => Cofinality/Ordinal.lean} (78%) diff --git a/Mathlib.lean b/Mathlib.lean index 1993c03a94..9f561a4db8 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -6960,7 +6960,8 @@ public import Mathlib.RingTheory.ZariskisMainTheorem public import Mathlib.SetTheory.Cardinal.Aleph public import Mathlib.SetTheory.Cardinal.Arithmetic public import Mathlib.SetTheory.Cardinal.Basic -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Basic +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal public import Mathlib.SetTheory.Cardinal.Continuum public import Mathlib.SetTheory.Cardinal.CountableCover public import Mathlib.SetTheory.Cardinal.Defs diff --git a/Mathlib/CategoryTheory/Presentable/IsCardinalFiltered.lean b/Mathlib/CategoryTheory/Presentable/IsCardinalFiltered.lean index 0f4a06aa09..1bf553db88 100644 --- a/Mathlib/CategoryTheory/Presentable/IsCardinalFiltered.lean +++ b/Mathlib/CategoryTheory/Presentable/IsCardinalFiltered.lean @@ -8,7 +8,7 @@ module public import Mathlib.CategoryTheory.Filtered.Final public import Mathlib.CategoryTheory.Limits.Shapes.WideEqualizers public import Mathlib.CategoryTheory.Comma.CardinalArrow -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal public import Mathlib.SetTheory.Cardinal.HasCardinalLT public import Mathlib.SetTheory.Cardinal.Arithmetic diff --git a/Mathlib/LinearAlgebra/Dimension/Finite.lean b/Mathlib/LinearAlgebra/Dimension/Finite.lean index fd04e5a7ed..0ffa7cd3a8 100644 --- a/Mathlib/LinearAlgebra/Dimension/Finite.lean +++ b/Mathlib/LinearAlgebra/Dimension/Finite.lean @@ -9,7 +9,7 @@ public import Mathlib.LinearAlgebra.Dimension.Constructions public import Mathlib.LinearAlgebra.Dimension.StrongRankCondition public import Mathlib.LinearAlgebra.Dimension.Subsingleton public import Mathlib.LinearAlgebra.FreeModule.Finite.Basic -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal /-! # Conditions for rank to be finite diff --git a/Mathlib/Order/Filter/Cocardinal.lean b/Mathlib/Order/Filter/Cocardinal.lean index abb0f750c1..c783b9c7c5 100644 --- a/Mathlib/Order/Filter/Cocardinal.lean +++ b/Mathlib/Order/Filter/Cocardinal.lean @@ -9,7 +9,7 @@ public import Mathlib.Order.Filter.Cofinite public import Mathlib.Order.Filter.CountableInter public import Mathlib.Order.Filter.CardinalInter public import Mathlib.SetTheory.Cardinal.Arithmetic -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal /-! # The cocardinal filter diff --git a/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean new file mode 100644 index 0000000000..020b1a98d7 --- /dev/null +++ b/Mathlib/SetTheory/Cardinal/Cofinality/Basic.lean @@ -0,0 +1,203 @@ +/- +Copyright (c) 2017 Mario Carneiro. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Mario Carneiro, Floris van Doorn, Violeta Hernández Palacios +-/ +module + +public import Mathlib.Order.Cofinal +public import Mathlib.SetTheory.Cardinal.Basic + +/-! +# Cofinality of an order + +This file contains the definition of the cofinality `Order.cof α` of an order. This is the smallest +cardinality of a cofinal subset. +-/ + +public noncomputable section + +open Function Cardinal Set Order + +universe u v w + +variable {α γ : Type u} {β : Type v} + +/-! ### Cofinality of orders -/ + +namespace Order +section Preorder +variable [Preorder α] + +variable (α) in +/-- The cofinality of a preorder is the smallest cardinality of a cofinal subset. -/ +def cof : Cardinal := + ⨅ s : {s : Set α // IsCofinal s}, #s + +theorem cof_le {s : Set α} (h : IsCofinal s) : cof α ≤ #s := + ciInf_le' (ι := {s : Set α // IsCofinal s}) _ ⟨s, h⟩ + +theorem le_lift_cof_iff {c : Cardinal.{max u v}} : + c ≤ lift.{v} (cof α) ↔ ∀ s : Set α, IsCofinal s → c ≤ lift.{v} #s := by + rw [cof, lift_iInf, le_ciInf_iff'] + simp + +theorem le_cof_iff {c : Cardinal} : c ≤ cof α ↔ ∀ s : Set α, IsCofinal s → c ≤ #s := by + simpa using @le_lift_cof_iff.{u, u} α _ c + +@[deprecated (since := "2026-02-18")] alias le_cof := le_cof_iff + +variable (α) in +theorem cof_eq : ∃ s : Set α, IsCofinal s ∧ #s = cof α := by + obtain ⟨s, hs⟩ := ciInf_mem fun s : {s : Set α // IsCofinal s} ↦ #s + exact ⟨s.1, s.2, hs⟩ + +variable (α) in +theorem cof_le_cardinalMk : cof α ≤ #α := + cof_le .univ |>.trans_eq mk_univ + +theorem cof_eq_zero_iff : cof α = 0 ↔ IsEmpty α := by + refine ⟨fun _ ↦ ?_, fun _ ↦ by simp [cof]⟩ + obtain ⟨s, hs, hs'⟩ := cof_eq α + simp_all [mk_eq_zero_iff, isCofinal_empty_iff] + +@[simp] +theorem cof_eq_zero [h : IsEmpty α] : cof α = 0 := + cof_eq_zero_iff.2 h + +theorem cof_ne_zero_iff : cof α ≠ 0 ↔ Nonempty α := by + simpa using cof_eq_zero_iff.not + +@[simp] +theorem cof_ne_zero [h : Nonempty α] : cof α ≠ 0 := + cof_ne_zero_iff.2 h + +theorem cof_eq_one_iff : cof α = 1 ↔ ∃ x : α, IsTop x := by + refine ⟨fun h ↦ ?_, fun ⟨t, ht⟩ ↦ ?_⟩ + · obtain ⟨s, hs, hs'⟩ := cof_eq α + rw [h, mk_set_eq_one_iff] at hs' + obtain ⟨t, rfl⟩ := hs' + use t + rwa [isCofinal_singleton_iff] at hs + · apply le_antisymm + · apply (cof_le (s := {t}) _).trans_eq (mk_singleton _) + rwa [isCofinal_singleton_iff] + · rw [Cardinal.one_le_iff_ne_zero, cof_ne_zero_iff] + use t + +@[simp] +theorem cof_eq_one [OrderTop α] : cof α = 1 := + cof_eq_one_iff.2 ⟨⊤, isTop_top⟩ + +theorem cof_ne_one_iff : cof α ≠ 1 ↔ NoTopOrder α := by + rw [← not_iff_not, not_not, noTopOrder_iff, cof_eq_one_iff] + simp + +@[simp] +theorem cof_ne_one [h : NoTopOrder α] : cof α ≠ 1 := + cof_ne_one_iff.2 h + +theorem cof_le_one_iff [Nonempty α] : cof α ≤ 1 ↔ ∃ x : α, IsTop x := by + rw [le_iff_lt_or_eq, Cardinal.lt_one_iff, cof_eq_one_iff] + simp + +theorem one_lt_cof_iff [Nonempty α] : 1 < cof α ↔ NoTopOrder α := by + rw [← not_iff_not, not_lt, noTopOrder_iff, cof_le_one_iff] + simp + +@[simp] +theorem one_lt_cof [Nonempty α] [h : NoTopOrder α] : 1 < cof α := + one_lt_cof_iff.2 h + +end Preorder + +section LinearOrder +variable [LinearOrder α] [LinearOrder β] [LinearOrder γ] + +theorem lift_cof_congr_of_strictMono {f : α → β} (hf : StrictMono f) (hf' : IsCofinal (range f)) : + lift.{v} (cof α) = lift.{u} (cof β) := by + apply le_antisymm <;> rw [le_lift_cof_iff] <;> intro s hs + · have H (x : s) : ∃ y : α, x ≤ f y := by simpa using hf' x + choose g hg using H + refine (lift_le.2 <| cof_le (s := range g) fun a ↦ ?_).trans mk_range_le_lift + obtain ⟨_, ⟨b, rfl⟩, hb⟩ := hf' (f a) + obtain ⟨c, hc, hc'⟩ := hs (f b) + refine ⟨_, Set.mem_range_self ⟨c, hc⟩, ?_⟩ + rw [← hf.le_iff_le] + exact hb.trans (hc'.trans (hg ⟨c, hc⟩)) + · exact (lift_le.2 <| cof_le (hs.image hf.monotone hf')).trans mk_image_le_lift + +theorem cof_congr_of_strictMono {f : α → γ} (hf : StrictMono f) (hf' : IsCofinal (range f)) : + cof α = cof γ := by + simpa using lift_cof_congr_of_strictMono hf hf' + +@[simp] +theorem cof_lt_aleph0_iff : Order.cof α < ℵ₀ ↔ Order.cof α ≤ 1 := by + refine ⟨fun h ↦ ?_, (lt_of_le_of_lt · one_lt_aleph0)⟩ + obtain ⟨s, hs, hs'⟩ := Order.cof_eq α + have hf : s.Finite := by + rw [Set.Finite, ← mk_lt_aleph0_iff] + exact hs'.trans_lt h + obtain ⟨t, ht, ht'⟩ := hf.exists_subsingleton_isCofinal hs + apply (cof_le ht').trans + simpa + +@[simp] +theorem aleph0_le_cof_iff : ℵ₀ ≤ Order.cof α ↔ 1 < Order.cof α := by + simp [← not_lt] + +@[simp] +theorem cof_eq_aleph0 [NoMaxOrder α] [Nonempty α] [Countable α] : cof α = ℵ₀ := + ((cof_le_cardinalMk _).trans mk_le_aleph0).antisymm (by simp) + +theorem cof_nat : cof ℕ = ℵ₀ := by simp + +end LinearOrder +end Order + +section Congr +variable [Preorder α] [Preorder β] [Preorder γ] + +theorem GaloisConnection.cof_le_lift {f : β → α} {g : α → β} (h : GaloisConnection f g) : + Cardinal.lift.{u} (Order.cof β) ≤ Cardinal.lift.{v} (Order.cof α) := by + rw [le_lift_cof_iff] + exact fun s hs ↦ (lift_le.2 <| cof_le (h.map_isCofinal hs)).trans mk_image_le_lift + +theorem GaloisConnection.cof_le {f : γ → α} {g : α → γ} (h : GaloisConnection f g) : + Order.cof γ ≤ Order.cof α := by + simpa using h.cof_le_lift + +theorem OrderIso.lift_cof_congr (f : α ≃o β) : + Cardinal.lift.{v} (Order.cof α) = Cardinal.lift.{u} (Order.cof β) := + f.to_galoisConnection.cof_le_lift.antisymm (f.symm.to_galoisConnection.cof_le_lift) + +@[deprecated (since := "2026-03-20")] alias OrderIso.lift_cof_eq := OrderIso.lift_cof_congr + +theorem OrderIso.cof_congr (f : α ≃o γ) : Order.cof α = Order.cof γ := by + simpa using f.lift_cof_congr + +@[deprecated (since := "2026-03-20")] alias OrderIso.cof_eq := OrderIso.cof_congr + +@[deprecated (since := "2026-02-18")] alias RelIso.cof_eq_lift := OrderIso.lift_cof_congr +@[deprecated (since := "2026-02-18")] alias RelIso.cof_eq := OrderIso.cof_congr + +end Congr + +/-- If the union of `s` is cofinal and `s` is smaller than the cofinality, then `s` has a cofinal +member. -/ +theorem isCofinal_of_isCofinal_sUnion {α : Type*} [LinearOrder α] {s : Set (Set α)} + (h₁ : IsCofinal (⋃₀ s)) (h₂ : #s < Order.cof α) : ∃ x ∈ s, IsCofinal x := by + contrapose! h₂ + simp_rw [not_isCofinal_iff] at h₂ + choose f hf using h₂ + refine (cof_le (s := range fun x ↦ f x.1 x.2) fun a ↦ ?_).trans mk_range_le + obtain ⟨b, ⟨t, ht, hb⟩, hab⟩ := h₁ a + simpa using ⟨t, ht, hab.trans (hf t ht b hb).le⟩ + +/-- If the union of the `ι`-indexed family `s` is cofinal and `ι` is smaller than the cofinality, +then `s` has a cofinal member. -/ +theorem isCofinal_of_isCofinal_iUnion {α : Type*} {ι} [LinearOrder α] {s : ι → Set α} + (h₁ : IsCofinal (⋃ i, s i)) (h₂ : #ι < Order.cof α) : ∃ i, IsCofinal (s i) := by + rw [← sUnion_range] at h₁ + obtain ⟨_, ⟨i, rfl⟩, h⟩ := isCofinal_of_isCofinal_sUnion h₁ (mk_range_le.trans_lt h₂) + exact ⟨i, h⟩ diff --git a/Mathlib/SetTheory/Cardinal/Cofinality.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Ordinal.lean similarity index 78% rename from Mathlib/SetTheory/Cardinal/Cofinality.lean rename to Mathlib/SetTheory/Cardinal/Cofinality/Ordinal.lean index 2c6f9e25f3..84be4fadcd 100644 --- a/Mathlib/SetTheory/Cardinal/Cofinality.lean +++ b/Mathlib/SetTheory/Cardinal/Cofinality/Ordinal.lean @@ -5,30 +5,25 @@ Authors: Mario Carneiro, Floris van Doorn, Violeta Hernández Palacios -/ module -public import Mathlib.Order.Cofinal public import Mathlib.SetTheory.Cardinal.Arithmetic +public import Mathlib.SetTheory.Cardinal.Cofinality.Basic public import Mathlib.SetTheory.Ordinal.FixedPoint /-! -# Cofinality +# Cofinality of an ordinal -This file contains the definition of cofinality of an order and an ordinal number. +This file contains the definition of the cofinality `Ordinal.cof o` of an ordinal. This is the +cofinality of the ordinal `o` when viewed as a linear order. -## Main Definitions - -* `Order.cof α` is the cofinality of a preorder. This is the smallest cardinality of a cofinal - subset. -* `Ordinal.cof o` is the cofinality of the ordinal `o` when viewed as a linear order. - -## Main Statements +## Main statements * `Cardinal.lt_power_cof_ord`: A consequence of König's theorem stating that `c < c ^ c.ord.cof` for `c ≥ ℵ₀`. -## Implementation Notes +## Implementation notes -* The cofinality is defined for ordinals. - If `c` is a cardinal number, its cofinality is `c.ord.cof`. +* We do not separately define the cofinality of a cardinal. If `c` is a cardinal number, you can + write its cofinality as `c.ord.cof`. -/ public noncomputable section @@ -40,185 +35,7 @@ universe u v w variable {α γ : Type u} {β : Type v} -/-! ### Cofinality of orders -/ - -namespace Order -section Preorder -variable [Preorder α] - -variable (α) in -/-- The cofinality of a preorder is the smallest cardinality of a cofinal subset. -/ -def cof : Cardinal := - ⨅ s : {s : Set α // IsCofinal s}, #s - -theorem cof_le {s : Set α} (h : IsCofinal s) : cof α ≤ #s := - ciInf_le' (ι := {s : Set α // IsCofinal s}) _ ⟨s, h⟩ - -theorem le_lift_cof_iff {c : Cardinal.{max u v}} : - c ≤ lift.{v} (cof α) ↔ ∀ s : Set α, IsCofinal s → c ≤ lift.{v} #s := by - rw [cof, lift_iInf, le_ciInf_iff'] - simp - -theorem le_cof_iff {c : Cardinal} : c ≤ cof α ↔ ∀ s : Set α, IsCofinal s → c ≤ #s := by - simpa using @le_lift_cof_iff.{u, u} α _ c - -@[deprecated (since := "2026-02-18")] alias le_cof := le_cof_iff - -variable (α) in -theorem cof_eq : ∃ s : Set α, IsCofinal s ∧ #s = cof α := by - obtain ⟨s, hs⟩ := ciInf_mem fun s : {s : Set α // IsCofinal s} ↦ #s - exact ⟨s.1, s.2, hs⟩ - -variable (α) in -theorem cof_le_cardinalMk : cof α ≤ #α := - cof_le .univ |>.trans_eq mk_univ - -theorem cof_eq_zero_iff : cof α = 0 ↔ IsEmpty α := by - refine ⟨fun _ ↦ ?_, fun _ ↦ by simp [cof]⟩ - obtain ⟨s, hs, hs'⟩ := cof_eq α - simp_all [mk_eq_zero_iff, isCofinal_empty_iff] - -@[simp] -theorem cof_eq_zero [h : IsEmpty α] : cof α = 0 := - cof_eq_zero_iff.2 h - -theorem cof_ne_zero_iff : cof α ≠ 0 ↔ Nonempty α := by - simpa using cof_eq_zero_iff.not - -@[simp] -theorem cof_ne_zero [h : Nonempty α] : cof α ≠ 0 := - cof_ne_zero_iff.2 h - -theorem cof_eq_one_iff : cof α = 1 ↔ ∃ x : α, IsTop x := by - refine ⟨fun h ↦ ?_, fun ⟨t, ht⟩ ↦ ?_⟩ - · obtain ⟨s, hs, hs'⟩ := cof_eq α - rw [h, mk_set_eq_one_iff] at hs' - obtain ⟨t, rfl⟩ := hs' - use t - rwa [isCofinal_singleton_iff] at hs - · apply le_antisymm - · apply (cof_le (s := {t}) _).trans_eq (mk_singleton _) - rwa [isCofinal_singleton_iff] - · rw [Cardinal.one_le_iff_ne_zero, cof_ne_zero_iff] - use t - -@[simp] -theorem cof_eq_one [OrderTop α] : cof α = 1 := - cof_eq_one_iff.2 ⟨⊤, isTop_top⟩ - -theorem cof_ne_one_iff : cof α ≠ 1 ↔ NoTopOrder α := by - rw [← not_iff_not, not_not, noTopOrder_iff, cof_eq_one_iff] - simp - -@[simp] -theorem cof_ne_one [h : NoTopOrder α] : cof α ≠ 1 := - cof_ne_one_iff.2 h - -theorem cof_le_one_iff [Nonempty α] : cof α ≤ 1 ↔ ∃ x : α, IsTop x := by - rw [le_iff_lt_or_eq, Cardinal.lt_one_iff, cof_eq_one_iff] - simp - -theorem one_lt_cof_iff [Nonempty α] : 1 < cof α ↔ NoTopOrder α := by - rw [← not_iff_not, not_lt, noTopOrder_iff, cof_le_one_iff] - simp - -@[simp] -theorem one_lt_cof [Nonempty α] [h : NoTopOrder α] : 1 < cof α := - one_lt_cof_iff.2 h - -end Preorder - -section LinearOrder -variable [LinearOrder α] [LinearOrder β] [LinearOrder γ] - -theorem lift_cof_congr_of_strictMono {f : α → β} (hf : StrictMono f) (hf' : IsCofinal (range f)) : - lift.{v} (cof α) = lift.{u} (cof β) := by - apply le_antisymm <;> rw [le_lift_cof_iff] <;> intro s hs - · have H (x : s) : ∃ y : α, x ≤ f y := by simpa using hf' x - choose g hg using H - refine (lift_le.2 <| cof_le (s := range g) fun a ↦ ?_).trans mk_range_le_lift - obtain ⟨_, ⟨b, rfl⟩, hb⟩ := hf' (f a) - obtain ⟨c, hc, hc'⟩ := hs (f b) - refine ⟨_, Set.mem_range_self ⟨c, hc⟩, ?_⟩ - rw [← hf.le_iff_le] - exact hb.trans (hc'.trans (hg ⟨c, hc⟩)) - · exact (lift_le.2 <| cof_le (hs.image hf.monotone hf')).trans mk_image_le_lift - -theorem cof_congr_of_strictMono {f : α → γ} (hf : StrictMono f) (hf' : IsCofinal (range f)) : - cof α = cof γ := by - simpa using lift_cof_congr_of_strictMono hf hf' - -@[simp] -theorem cof_lt_aleph0_iff : Order.cof α < ℵ₀ ↔ Order.cof α ≤ 1 := by - refine ⟨fun h ↦ ?_, (lt_of_le_of_lt · one_lt_aleph0)⟩ - obtain ⟨s, hs, hs'⟩ := Order.cof_eq α - have hf : s.Finite := by - rw [Set.Finite, ← mk_lt_aleph0_iff] - exact hs'.trans_lt h - obtain ⟨t, ht, ht'⟩ := hf.exists_subsingleton_isCofinal hs - apply (cof_le ht').trans - simpa - -@[simp] -theorem aleph0_le_cof_iff : ℵ₀ ≤ Order.cof α ↔ 1 < Order.cof α := by - simp [← not_lt] - -@[simp] -theorem cof_eq_aleph0 [NoMaxOrder α] [Nonempty α] [Countable α] : cof α = ℵ₀ := - ((cof_le_cardinalMk _).trans mk_le_aleph0).antisymm (by simp) - -theorem cof_nat : cof ℕ = ℵ₀ := by simp -theorem cof_int : cof ℤ = ℵ₀ := by simp - -end LinearOrder -end Order - -section Congr -variable [Preorder α] [Preorder β] [Preorder γ] - -theorem GaloisConnection.cof_le_lift {f : β → α} {g : α → β} (h : GaloisConnection f g) : - Cardinal.lift.{u} (Order.cof β) ≤ Cardinal.lift.{v} (Order.cof α) := by - rw [le_lift_cof_iff] - exact fun s hs ↦ (lift_le.2 <| cof_le (h.map_isCofinal hs)).trans mk_image_le_lift - -theorem GaloisConnection.cof_le {f : γ → α} {g : α → γ} (h : GaloisConnection f g) : - Order.cof γ ≤ Order.cof α := by - simpa using h.cof_le_lift - -theorem OrderIso.lift_cof_congr (f : α ≃o β) : - Cardinal.lift.{v} (Order.cof α) = Cardinal.lift.{u} (Order.cof β) := - f.to_galoisConnection.cof_le_lift.antisymm (f.symm.to_galoisConnection.cof_le_lift) - -@[deprecated (since := "2026-03-20")] alias OrderIso.lift_cof_eq := OrderIso.lift_cof_congr - -theorem OrderIso.cof_congr (f : α ≃o γ) : Order.cof α = Order.cof γ := by - simpa using f.lift_cof_congr - -@[deprecated (since := "2026-03-20")] alias OrderIso.cof_eq := OrderIso.cof_congr - -@[deprecated (since := "2026-02-18")] alias RelIso.cof_eq_lift := OrderIso.lift_cof_congr -@[deprecated (since := "2026-02-18")] alias RelIso.cof_eq := OrderIso.cof_congr - -end Congr - -/-- If the union of `s` is cofinal and `s` is smaller than the cofinality, then `s` has a cofinal -member. -/ -theorem isCofinal_of_isCofinal_sUnion {α : Type*} [LinearOrder α] {s : Set (Set α)} - (h₁ : IsCofinal (⋃₀ s)) (h₂ : #s < Order.cof α) : ∃ x ∈ s, IsCofinal x := by - contrapose! h₂ - simp_rw [not_isCofinal_iff] at h₂ - choose f hf using h₂ - refine (cof_le (s := range fun x ↦ f x.1 x.2) fun a ↦ ?_).trans mk_range_le - obtain ⟨b, ⟨t, ht, hb⟩, hab⟩ := h₁ a - simpa using ⟨t, ht, hab.trans (hf t ht b hb).le⟩ - -/-- If the union of the `ι`-indexed family `s` is cofinal and `ι` is smaller than the cofinality, -then `s` has a cofinal member. -/ -theorem isCofinal_of_isCofinal_iUnion {α : Type*} {ι} [LinearOrder α] {s : ι → Set α} - (h₁ : IsCofinal (⋃ i, s i)) (h₂ : #ι < Order.cof α) : ∃ i, IsCofinal (s i) := by - rw [← sUnion_range] at h₁ - obtain ⟨_, ⟨i, rfl⟩, h⟩ := isCofinal_of_isCofinal_sUnion h₁ (mk_range_le.trans_lt h₂) - exact ⟨i, h⟩ +theorem Order.cof_int : cof ℤ = ℵ₀ := by simp /-! ### Cofinality of ordinals -/ diff --git a/Mathlib/SetTheory/Cardinal/Regular.lean b/Mathlib/SetTheory/Cardinal/Regular.lean index a217f53550..d552b46b5e 100644 --- a/Mathlib/SetTheory/Cardinal/Regular.lean +++ b/Mathlib/SetTheory/Cardinal/Regular.lean @@ -5,7 +5,7 @@ Authors: Mario Carneiro, Floris van Doorn, Violeta Hernández Palacios, Nir Paz -/ module -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal public import Mathlib.SetTheory.Ordinal.FixedPoint /-! diff --git a/Mathlib/SetTheory/Ordinal/FundamentalSequence.lean b/Mathlib/SetTheory/Ordinal/FundamentalSequence.lean index f76ca692f4..dac122e01d 100644 --- a/Mathlib/SetTheory/Ordinal/FundamentalSequence.lean +++ b/Mathlib/SetTheory/Ordinal/FundamentalSequence.lean @@ -5,7 +5,7 @@ Authors: Violeta Hernández Palacios, Mario Carneiro -/ module -public import Mathlib.SetTheory.Cardinal.Cofinality +public import Mathlib.SetTheory.Cardinal.Cofinality.Ordinal /-! # Fundamental sequences From bac93a83bd9e32944fa9678fa756f5ca311be93b Mon Sep 17 00:00:00 2001 From: Antoine Chambert-Loir Date: Sun, 10 May 2026 00:45:45 +0000 Subject: [PATCH 10/65] feat(Tactic/Translate/ToAdditive.lean): add conGen/AddConGen to the to_additive tactic (#38525) This PR adds the pair "conGen", "AddConGen" to the additive tactic and capitalizes the second of some pairs that should have been. --- Mathlib/Algebra/Exact.lean | 2 +- Mathlib/GroupTheory/Congruence/Defs.lean | 14 +++++++------- Mathlib/Tactic/Translate/ToAdditive.lean | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Mathlib/Algebra/Exact.lean b/Mathlib/Algebra/Exact.lean index 0a9beae3a2..0457b644d8 100644 --- a/Mathlib/Algebra/Exact.lean +++ b/Mathlib/Algebra/Exact.lean @@ -40,7 +40,7 @@ namespace Function variable (f : M → N) (g : N → P) (g' : P → P') /-- The maps `f` and `g` form an exact pair: `g y = 1` iff `y` belongs to the image of `f`. -/ -@[to_additive Exact /-- The maps `f` and `g` form an exact pair: +@[to_additive /-- The maps `f` and `g` form an exact pair: `g y = 0` iff `y` belongs to the image of `f`. -/] def MulExact [One P] : Prop := ∀ y, g y = 1 ↔ y ∈ Set.range f diff --git a/Mathlib/GroupTheory/Congruence/Defs.lean b/Mathlib/GroupTheory/Congruence/Defs.lean index 831df828db..bccc972653 100644 --- a/Mathlib/GroupTheory/Congruence/Defs.lean +++ b/Mathlib/GroupTheory/Congruence/Defs.lean @@ -98,7 +98,7 @@ inductive ConGen.Rel [Mul M] (r : M → M → Prop) : M → M → Prop /-- The inductively defined smallest multiplicative congruence relation containing a given binary relation. -/ -@[to_additive addConGen /-- The inductively defined smallest additive congruence relation containing +@[to_additive /-- The inductively defined smallest additive congruence relation containing a given binary relation. -/] def conGen [Mul M] (r : M → M → Prop) : Con M := ⟨⟨ConGen.Rel r, ⟨ConGen.Rel.refl, ConGen.Rel.symm, ConGen.Rel.trans⟩⟩, ConGen.Rel.mul⟩ @@ -394,7 +394,7 @@ theorem inf_iff_and {c d : Con M} {x y} : (c ⊓ d) x y ↔ c x y ∧ d x y := /-- The inductively defined smallest congruence relation containing a binary relation `r` equals the infimum of the set of congruence relations containing `r`. -/ -@[to_additive addConGen_eq /-- The inductively defined smallest additive congruence relation +@[to_additive /-- The inductively defined smallest additive congruence relation containing a binary relation `r` equals the infimum of the set of additive congruence relations containing `r`. -/] theorem conGen_eq (r : M → M → Prop) : conGen r = sInf { s : Con M | ∀ x y, r x y → s x y } := @@ -411,14 +411,14 @@ theorem conGen_eq (r : M → M → Prop) : conGen r = sInf { s : Con M | ∀ x y /-- The smallest congruence relation containing a binary relation `r` is contained in any congruence relation containing `r`. -/ -@[to_additive addConGen_le /-- The smallest additive congruence relation containing a binary +@[to_additive /-- The smallest additive congruence relation containing a binary relation `r` is contained in any additive congruence relation containing `r`. -/] theorem conGen_le {r : M → M → Prop} {c : Con M} (h : ∀ x y, r x y → c x y) : conGen r ≤ c := by rw [conGen_eq]; exact sInf_le h /-- Given binary relations `r, s` with `r` contained in `s`, the smallest congruence relation containing `s` contains the smallest congruence relation containing `r`. -/ -@[to_additive addConGen_mono /-- Given binary relations `r, s` with `r` contained in `s`, the +@[to_additive /-- Given binary relations `r, s` with `r` contained in `s`, the smallest additive congruence relation containing `s` contains the smallest additive congruence relation containing `r`. -/] theorem conGen_mono {r s : M → M → Prop} (h : ∀ x y, r x y → s x y) : conGen r ≤ conGen s := @@ -432,13 +432,13 @@ theorem conGen_of_con (c : Con M) : conGen c = c := /-- The map sending a binary relation to the smallest congruence relation in which it is contained is idempotent. -/ -@[to_additive addConGen_idem /-- The map sending a binary relation to the smallest additive +@[to_additive /-- The map sending a binary relation to the smallest additive congruence relation in which it is contained is idempotent. -/] theorem conGen_idem (r : M → M → Prop) : conGen (conGen r) = conGen r := by simp /-- The supremum of congruence relations `c, d` equals the smallest congruence relation containing the binary relation '`x` is related to `y` by `c` or `d`'. -/ -@[to_additive sup_eq_addConGen /-- The supremum of additive congruence relations `c, d` equals the +@[to_additive /-- The supremum of additive congruence relations `c, d` equals the smallest additive congruence relation containing the binary relation '`x` is related to `y` by `c` or `d`'. -/] theorem sup_eq_conGen (c d : Con M) : c ⊔ d = conGen fun x y => c x y ∨ d x y := by @@ -454,7 +454,7 @@ theorem sup_def {c d : Con M} : c ⊔ d = conGen (⇑c ⊔ ⇑d) := by rw [sup_e /-- The supremum of a set of congruence relations `S` equals the smallest congruence relation containing the binary relation 'there exists `c ∈ S` such that `x` is related to `y` by `c`'. -/ -@[to_additive sSup_eq_addConGen /-- The supremum of a set of additive congruence relations `S` +@[to_additive /-- The supremum of a set of additive congruence relations `S` equals the smallest additive congruence relation containing the binary relation 'there exists `c ∈ S` such that `x` is related to `y` by `c`'. -/] theorem sSup_eq_conGen (S : Set (Con M)) : diff --git a/Mathlib/Tactic/Translate/ToAdditive.lean b/Mathlib/Tactic/Translate/ToAdditive.lean index ce6000d5cc..fa0829532e 100644 --- a/Mathlib/Tactic/Translate/ToAdditive.lean +++ b/Mathlib/Tactic/Translate/ToAdditive.lean @@ -378,14 +378,15 @@ def abbreviationDict : Std.HashMap String String := .ofList [ ("subNegZeroAddMonoid", "SubNegZeroMonoid"), ("modularCharacter", "AddModularCharacter"), ("isQuotientCoveringMap", "IsAddQuotientCoveringMap"), - ("addExact", "exact"), + ("addExact", "Exact"), ("isMonHom", "IsAddMonHom"), ("mapMon", "MapAddMon"), ("monObj", "AddMonObj"), ("isModHom", "IsAddModHom"), ("mapMod", "MapAddMod"), ("modObj", "AddModObj"), - ("yonedaMon", "yonedaAddMon")] + ("yonedaMon", "YonedaAddMon"), + ("conGen", "AddConGen")] @[inherit_doc GuessName.GuessNameExt] initialize guessNameExt : GuessName.GuessNameExt ← From 8431d924ff40bacbe64b6be1952ea8a36e351b47 Mon Sep 17 00:00:00 2001 From: Yan Yablonovskiy <186670707+YanYablonovskiy@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:47 +0000 Subject: [PATCH 11/65] feat(Order): OrderType cardinality lemmas (#38846) Adding some basic lemmas for the cardinality of an order type. Co-authored-by: YanYablonovskiy --- Mathlib/Order/Types/Arithmetic.lean | 32 +++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Mathlib/Order/Types/Arithmetic.lean b/Mathlib/Order/Types/Arithmetic.lean index e186231372..fd560d56d1 100644 --- a/Mathlib/Order/Types/Arithmetic.lean +++ b/Mathlib/Order/Types/Arithmetic.lean @@ -6,11 +6,8 @@ Authors: Yan Yablonovskiy module public import Mathlib.Data.Real.Basic -public import Mathlib.Order.CompleteBooleanAlgebra -public import Mathlib.Order.Fin.Basic -public import Mathlib.Order.Hom.Lex -public import Mathlib.Order.OmegaCompletePartialOrder public import Mathlib.Order.Types.Defs +public import Mathlib.SetTheory.Cardinal.Order /-! @@ -101,6 +98,33 @@ instance : Monoid OrderType.{u} where simp only [show 1 = type PUnit by rfl, ← type_lex_prod] exact (Prod.Lex.uniqueProd PUnit α).type_congr) +section Cardinal + +open Cardinal + +/-- The cardinal of an `OrderType` is the cardinality of any type on which a relation +with that order type is defined. -/ +def card (o : OrderType) : Cardinal := + o.liftOn (fun α _ ↦ #α) + fun _ _ _ _ hab ↦ mk_congr (type_eq_type.mp hab).some.toEquiv + +@[simp] +theorem card_type {α : Type u} [LinearOrder α] : card (type α) = #α := by + rw [card, liftOn_type] + +@[gcongr] +theorem card_mono {o₁ o₂ : OrderType} : o₁ ≤ o₂ → card o₁ ≤ card o₂ := + inductionOn₂ o₁ o₂ fun _ _ _ _ hle ↦ by + simp [card, (type_le_type_iff.mp hle).some.cardinal_le] + +theorem card_monotone : Monotone card := @card_mono + +@[simp] theorem card_zero : card 0 = 0 := by simpa using card_type (α := PEmpty) + +@[simp] theorem card_one : card 1 = 1 := by simpa using card_type (α := PUnit) + +end Cardinal + instance (n : Nat) : OfNat OrderType n where ofNat := Fin n |> type From 93049402448a7c02deba710b6a6aeefd44cd2c17 Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:49 +0000 Subject: [PATCH 12/65] feat(Order/ConditionallyCompleteLattice/Indexed): `ciSup_mono'` for `ConditionallyCompleteLattice` (#38854) Usually a primed version of a sup/inf theorem is like the unprimed version but for `ConditionallyCompleteLinearOrderBot` which can remove `Nonempty` assumptions. `ciSup_mono'` is different from its unprimed version and it's missing `ConditionallyCompleteLattice` versions. We add these and rename `ciSup_mono'` to `ciSup_mono_of_forall_exists'`. --- Mathlib/LinearAlgebra/Dimension/Basic.lean | 4 ++-- .../ConditionallyCompleteLattice/Indexed.lean | 16 +++++++++++++--- Mathlib/SetTheory/Cardinal/Basic.lean | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Mathlib/LinearAlgebra/Dimension/Basic.lean b/Mathlib/LinearAlgebra/Dimension/Basic.lean index b8a9406870..4efb57fcd9 100644 --- a/Mathlib/LinearAlgebra/Dimension/Basic.lean +++ b/Mathlib/LinearAlgebra/Dimension/Basic.lean @@ -189,7 +189,7 @@ theorem lift_rank_le_of_injective_injectiveₛ (i : R' → R) (j : M →+ M') (hc : ∀ (r : R') (m : M), j (i r • m) = r • j m) : lift.{v'} (Module.rank R M) ≤ lift.{v} (Module.rank R' M') := by simp_rw [Module.rank, lift_iSup bddAbove_of_small] - exact ciSup_mono' bddAbove_of_small fun ⟨s, h⟩ ↦ ⟨⟨j '' s, + exact ciSup_mono_of_forall_exists' bddAbove_of_small fun ⟨s, h⟩ ↦ ⟨⟨j '' s, LinearIndepOn.id_image (h.linearIndependent.map_of_injective_injectiveₛ i j hi hj hc)⟩, lift_mk_le'.mpr ⟨(Equiv.Set.image j s hj).toEmbedding⟩⟩ @@ -273,7 +273,7 @@ theorem lift_rank_le_of_injective_injective [AddCommGroup M'] [Module R' M'] (hc : ∀ (r : R') (m : M), j (i r • m) = r • j m) : lift.{v'} (Module.rank R M) ≤ lift.{v} (Module.rank R' M') := by simp_rw [Module.rank, lift_iSup bddAbove_of_small] - exact ciSup_mono' bddAbove_of_small fun ⟨s, h⟩ ↦ + exact ciSup_mono_of_forall_exists' bddAbove_of_small fun ⟨s, h⟩ ↦ ⟨⟨j '' s, LinearIndepOn.id_image <| h.linearIndependent.map_of_injective_injective i j hi (fun _ _ ↦ hj <| by rwa [j.map_zero]) hc⟩, lift_mk_le'.mpr ⟨(Equiv.Set.image j s hj).toEmbedding⟩⟩ diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean index 317cf77e48..3fc7aecb99 100644 --- a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean +++ b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean @@ -188,6 +188,14 @@ theorem ciInf_le {f : ι → α} (H : BddBelow (range f)) (c : ι) : iInf f ≤ theorem ciInf_le_of_le {f : ι → α} (H : BddBelow (range f)) (c : ι) (h : f c ≤ a) : iInf f ≤ a := le_ciSup_of_le (α := αᵒᵈ) H c h +theorem ciSup_mono_of_forall_exists {ι'} [Nonempty ι] {f : ι → α} {g : ι' → α} + (hg : BddAbove <| range g) (h : ∀ i, ∃ i', f i ≤ g i') : ⨆ i, f i ≤ ⨆ i', g i' := + ciSup_le fun i ↦ h i |>.elim <| le_ciSup_of_le hg + +theorem ciInf_mono_of_forall_exists {ι'} [Nonempty ι'] {f : ι → α} {g : ι' → α} + (hf : BddBelow <| range f) (h : ∀ i', ∃ i, f i ≤ g i') : ⨅ i, f i ≤ ⨅ i', g i' := + ciSup_mono_of_forall_exists (α := αᵒᵈ) hf h + /-- If the set of all `f i j` is bounded below, then so is the set of the infimums of every row -/ theorem BddBelow.range_iInf_of_iUnion_range {κ : ι → Sort*} {f : ∀ i, κ i → α} (H : BddBelow <| ⋃ i, range (f i)) : BddBelow <| range fun i ↦ ⨅ j, f i j := by @@ -491,9 +499,11 @@ theorem exists_lt_of_lt_ciSup' {f : ι → α} {a : α} (h : a < ⨆ i, f i) : contrapose! h exact ciSup_le' h -theorem ciSup_mono' {ι'} {f : ι → α} {g : ι' → α} (hg : BddAbove (range g)) - (h : ∀ i, ∃ i', f i ≤ g i') : iSup f ≤ iSup g := - ciSup_le' fun i => Exists.elim (h i) (le_ciSup_of_le hg) +theorem ciSup_mono_of_forall_exists' {ι'} {f : ι → α} {g : ι' → α} (hg : BddAbove <| range g) + (h : ∀ i, ∃ i', f i ≤ g i') : ⨆ i, f i ≤ ⨆ i', g i' := + ciSup_le' fun i ↦ h i |>.elim <| le_ciSup_of_le hg + +@[deprecated (since := "2026-05-03")] alias ciSup_mono' := ciSup_mono_of_forall_exists' lemma ciSup_or' (p q : Prop) (f : p ∨ q → α) : ⨆ (h : p ∨ q), f h = (⨆ h : p, f (.inl h)) ⊔ ⨆ h : q, f (.inr h) := by diff --git a/Mathlib/SetTheory/Cardinal/Basic.lean b/Mathlib/SetTheory/Cardinal/Basic.lean index 0447a2c18a..b45d98e570 100644 --- a/Mathlib/SetTheory/Cardinal/Basic.lean +++ b/Mathlib/SetTheory/Cardinal/Basic.lean @@ -247,7 +247,7 @@ theorem lift_iSup_le_lift_iSup {ι : Type v} {ι' : Type v'} {f : ι → Cardina {f' : ι' → Cardinal.{w'}} (hf : BddAbove (range f)) (hf' : BddAbove (range f')) {g : ι → ι'} (h : ∀ i, lift.{w'} (f i) ≤ lift.{w} (f' (g i))) : lift.{w'} (iSup f) ≤ lift.{w} (iSup f') := by rw [lift_iSup hf, lift_iSup hf'] - exact ciSup_mono' (bddAbove_range_comp.{_, _, w} hf' _) fun i => ⟨_, h i⟩ + exact ciSup_mono_of_forall_exists' (bddAbove_range_comp.{_, _, w} hf' _) fun i => ⟨_, h i⟩ /-- A variant of `lift_iSup_le_lift_iSup` with universes specialized via `w = v` and `w' = v'`. This is sometimes necessary to avoid universe unification issues. -/ From 7d6fdac18478cb1168f5ccccf60736c16ae471ec Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:51 +0000 Subject: [PATCH 13/65] feat(Order/ConditionallyCompleteLattice/Indexed): conditional versions of `iSup_exists`/`iSup_and` (#38857) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For `iSup_exists` we can only get `≤` in `ConditionallyCompleteLattice`, and equality in `ConditionallyCompleteLinearOrderBot`. --- .../ConditionallyCompleteLattice/Indexed.lean | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean index 3fc7aecb99..53552821c5 100644 --- a/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean +++ b/Mathlib/Order/ConditionallyCompleteLattice/Indexed.lean @@ -368,6 +368,26 @@ lemma ciInf_image {ι ι' : Type*} {s : Set ι} {f : ι → ι'} {g : ι' → α ⨅ i ∈ (f '' s), g i = ⨅ x ∈ s, g (f x) := ciSup_image (α := αᵒᵈ) hf hg' +/-- Note that equality need not hold: consider `ι := Bool, p := (·), α := ℤ, f := fun _ ↦ -1`, +then the LHS is `-1` but the RHS is `-1 ⊔ sSup ∅ = -1 ⊔ 0 = 0`. -/ +theorem ciSup_exists_le {p : ι → Prop} {f : Exists p → α} : ⨆ ih, f ih ≤ ⨆ (i) (h), f ⟨i, h⟩ := by + by_cases! h : Exists p + · have : Nonempty <| Exists p := ⟨h⟩ + refine ciSup_le fun ⟨i, hi⟩ ↦ le_ciSup₂ (f := fun _ _ ↦ _) ⟨f ⟨i, hi⟩, ?_⟩ i hi + rintro _ ⟨_, ⟨j, rfl⟩, ⟨hj, rfl⟩⟩ + rfl + · cases isEmpty_or_nonempty ι <;> + simp [h, iSup_of_empty', ciSup_const] + +theorem le_ciInf_exists {p : ι → Prop} {f : Exists p → α} : ⨅ (i) (h), f ⟨i, h⟩ ≤ ⨅ ih, f ih := + ciSup_exists_le (α := αᵒᵈ) + +theorem ciSup_and {p q : Prop} {f : p ∧ q → α} : ⨆ ih, f ih = ⨆ (h₁) (h₂), f ⟨h₁, h₂⟩ := by + by_cases hp : p <;> by_cases hq : q <;> simp [hp, hq, iSup_of_empty'] + +theorem ciInf_and {p q : Prop} {f : p ∧ q → α} : ⨅ ih, f ih = ⨅ (h₁) (h₂), f ⟨h₁, h₂⟩ := + ciSup_and (α := αᵒᵈ) + end ConditionallyCompleteLattice section ConditionallyCompleteLinearOrder @@ -505,6 +525,10 @@ theorem ciSup_mono_of_forall_exists' {ι'} {f : ι → α} {g : ι' → α} (hg @[deprecated (since := "2026-05-03")] alias ciSup_mono' := ciSup_mono_of_forall_exists' +theorem ciSup_exists {p : ι → Prop} {f : Exists p → α} : ⨆ ih, f ih = ⨆ (i) (h), f ⟨i, h⟩ := by + refine le_antisymm ciSup_exists_le <| ciSup_le' fun i ↦ ciSup_le' fun hi ↦ ?_ + simp [show Exists p from ⟨i, hi⟩] + lemma ciSup_or' (p q : Prop) (f : p ∨ q → α) : ⨆ (h : p ∨ q), f h = (⨆ h : p, f (.inl h)) ⊔ ⨆ h : q, f (.inr h) := by by_cases hp : p <;> From c495aa5cc0f2a05d66ee5b4f845f7f2289ab69a1 Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:53 +0000 Subject: [PATCH 14/65] feat(Order/ConditionallyCompleteLattice/Basic): `ConditionallyCompleteLinearOrderBot` versions of `csSup_union`/`csSup_inter_le`/`csSup_insert` (#38858) `ConditionallyCompleteLinearOrderBot` lets us drop all the `Set.Nonempty` assumptions. --- .../ConditionallyCompleteLattice/Basic.lean | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean index 34aedc3509..fecde492ec 100644 --- a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean +++ b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean @@ -471,6 +471,14 @@ theorem csInf_eq_csInf_of_forall_exists_le {s t : Set α} sInf s = sInf t := csSup_eq_csSup_of_forall_exists_le (α := αᵒᵈ) hs ht +theorem csSup_union_le (s t : Set α) : sSup (s ∪ t) ≤ sSup s ⊔ sSup t := by + rcases s.eq_empty_or_nonempty with (rfl | hs) + · simp + rcases t.eq_empty_or_nonempty with (rfl | ht) + · simp + by_cases BddAbove (s ∪ t) <;> + grind [csSup_union, bddAbove_union, csSup_of_not_bddAbove] + lemma sSup_iUnion_Iic (f : ι → α) : sSup (⋃ (i : ι), Iic (f i)) = ⨆ i, f i := by apply csSup_eq_csSup_of_forall_exists_le · rintro x ⟨-, ⟨i, rfl⟩, hi⟩ @@ -588,6 +596,21 @@ theorem csSup_le_csSup' {s t : Set α} (h₁ : BddAbove t) (h₂ : s ⊆ t) : sS exact bot_le · exact csSup_le_csSup h₁ h h₂ +variable {t : Set α} + +theorem csSup_union' (hs : BddAbove s := by bddDefault) (ht : BddAbove t := by bddDefault) : + sSup (s ∪ t) = sSup s ⊔ sSup t := by + rcases s.eq_empty_or_nonempty with (rfl | hne) + · simp + exact (isLUB_csSup' hs |>.union <| isLUB_csSup' ht).csSup_eq hne.inl + +theorem csSup_inter_le' (hs : BddAbove s := by bddDefault) (ht : BddAbove t := by bddDefault) : + sSup (s ∩ t) ≤ sSup s ⊓ sSup t := + csSup_le' fun _ hx ↦ le_inf (le_csSup hs hx.left) (le_csSup ht hx.right) + +theorem csSup_insert' (hs : BddAbove s := by bddDefault) : sSup (insert a s) = a ⊔ sSup s := + isLUB_csSup' hs |>.insert a |>.csSup_eq <| insert_nonempty a s + end ConditionallyCompleteLinearOrderBot namespace WithTop From d8cbbf99f29ff4cdd421c0152693f4393c051230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 00:45:55 +0000 Subject: [PATCH 15/65] feat(Order/SuccPred/LinearLocallyFinite): `StrictMono toZ` (#39014) We prove that `toZ` is strictly monotonic, and golf/deprecate other theorems in this file using this. --- .../Order/SuccPred/LinearLocallyFinite.lean | 88 +++++++------------ 1 file changed, 31 insertions(+), 57 deletions(-) diff --git a/Mathlib/Order/SuccPred/LinearLocallyFinite.lean b/Mathlib/Order/SuccPred/LinearLocallyFinite.lean index eb6e8f5d04..c2becb30a6 100644 --- a/Mathlib/Order/SuccPred/LinearLocallyFinite.lean +++ b/Mathlib/Order/SuccPred/LinearLocallyFinite.lean @@ -292,7 +292,9 @@ theorem toZ_iterate_pred_of_not_isMin (n : ℕ) (hn : ¬IsMin (pred^[n] i0)) : · suffices IsMin (pred^[n.succ] i0) from absurd this hn exact isMin_iterate_pred_of_eq_of_ne h_eq.symm (Ne.symm hmn) -theorem le_of_toZ_le {j : ι} (h_le : toZ i0 i ≤ toZ i0 j) : i ≤ j := by +theorem toZ_strictMono : StrictMono (toZ i0) := by + intro j i h_le + contrapose! h_le rcases le_or_gt i0 i with hi | hi <;> rcases le_or_gt i0 j with hj | hj · rw [← iterate_succ_toZ i hi, ← iterate_succ_toZ j hj] exact Monotone.monotone_iterate_of_le_map succ_mono (le_succ _) (Int.toNat_le_toNat h_le) @@ -302,54 +304,29 @@ theorem le_of_toZ_le {j : ι} (h_le : toZ i0 i ≤ toZ i0 j) : i ≤ j := by refine Monotone.antitone_iterate_of_map_le pred_mono (pred_le _) (Int.toNat_le_toNat ?_) exact Int.neg_le_neg h_le -theorem toZ_mono {i j : ι} (h_le : i ≤ j) : toZ i0 i ≤ toZ i0 j := by - by_cases hi_max : IsMax i - · rw [le_antisymm h_le (hi_max h_le)] - by_cases hj_min : IsMin j - · rw [le_antisymm h_le (hj_min h_le)] - rcases le_or_gt i0 i with hi | hi <;> rcases le_or_gt i0 j with hj | hj - · let m := Nat.find (exists_succ_iterate_of_le h_le) - have hm : succ^[m] i = j := Nat.find_spec (exists_succ_iterate_of_le h_le) - have hj_eq : j = succ^[(toZ i0 i).toNat + m] i0 := by - rw [← hm, add_comm] - nth_rw 1 [← iterate_succ_toZ i hi] - rw [Function.iterate_add] - rfl - by_contra h - obtain hm0 | hm0 : m = 0 ∨ 1 ≤ m := by lia - · rw [hm0, Function.iterate_zero, id] at hm - rw [hm] at h - exact h (le_of_eq rfl) - refine hi_max (max_of_succ_le (le_trans ?_ (@le_of_toZ_le _ _ _ _ _ i0 j i ?_))) - · have h_succ_le : succ^[(toZ i0 i).toNat + 1] i0 ≤ j := by - rw [hj_eq] - exact Monotone.monotone_iterate_of_le_map succ_mono (le_succ i0) (by gcongr) - rwa [Function.iterate_succ', Function.comp_apply, iterate_succ_toZ i hi] at h_succ_le - · exact le_of_not_ge h - · exact absurd h_le (not_le.mpr (hj.trans_le hi)) - · exact (toZ_neg hi).le.trans (toZ_nonneg hj) - · let m := Nat.find (exists_pred_iterate_of_le (α := ι) h_le) - have hm : pred^[m] j = i := Nat.find_spec (exists_pred_iterate_of_le (α := ι) h_le) - have hj_eq : i = pred^[(-toZ i0 j).toNat + m] i0 := by - rw [← hm, add_comm] - nth_rw 1 [← iterate_pred_toZ j hj] - rw [Function.iterate_add] - rfl - by_contra h - obtain hm0 | hm0 : m = 0 ∨ 1 ≤ m := by lia - · rw [hm0, Function.iterate_zero, id] at hm - rw [hm] at h - exact h (le_of_eq rfl) - refine hj_min (min_of_le_pred ?_) - refine (@le_of_toZ_le _ _ _ _ _ i0 j i ?_).trans ?_ - · exact le_of_not_ge h - · have h_le_pred : i ≤ pred^[(-toZ i0 j).toNat + 1] i0 := by - rw [hj_eq] - exact Monotone.antitone_iterate_of_map_le pred_mono (pred_le i0) (by gcongr) - rwa [Function.iterate_succ', Function.comp_apply, iterate_pred_toZ j hj] at h_le_pred - -theorem toZ_le_iff (i j : ι) : toZ i0 i ≤ toZ i0 j ↔ i ≤ j := - ⟨le_of_toZ_le, toZ_mono⟩ +theorem injective_toZ : Function.Injective (toZ i0) := + toZ_strictMono.injective + +@[simp] +theorem toZ_le_toZ {i j : ι} : toZ i0 i ≤ toZ i0 j ↔ i ≤ j := + toZ_strictMono.le_iff_le + +@[deprecated (since := "2026-05-07")] +alias toZ_le_iff := toZ_le_toZ + +@[deprecated toZ_le_toZ (since := "2026-05-06")] +alias ⟨le_of_toZ_le, toZ_mono⟩ := toZ_le_toZ + +@[simp] +theorem toZ_lt_toZ {i j : ι} : toZ i0 i < toZ i0 j ↔ i < j := + toZ_strictMono.lt_iff_lt + +@[deprecated (since := "2026-05-07")] +alias toZ_lt_iff := toZ_lt_toZ + +@[simp] +theorem toZ_inj {i j : ι} : toZ i0 i = toZ i0 j ↔ i = j := + injective_toZ.eq_iff theorem toZ_iterate_succ [NoMaxOrder ι] (n : ℕ) : toZ i0 (succ^[n] i0) = n := toZ_iterate_succ_of_not_isMax n (not_isMax _) @@ -357,9 +334,6 @@ theorem toZ_iterate_succ [NoMaxOrder ι] (n : ℕ) : toZ i0 (succ^[n] i0) = n := theorem toZ_iterate_pred [NoMinOrder ι] (n : ℕ) : toZ i0 (pred^[n] i0) = -n := toZ_iterate_pred_of_not_isMin n (not_isMin _) -theorem injective_toZ : Function.Injective (toZ i0) := - fun _ _ h ↦ le_antisymm (le_of_toZ_le h.le) (le_of_toZ_le h.symm.le) - end toZ section OrderIso @@ -370,7 +344,7 @@ variable [SuccOrder ι] [PredOrder ι] [IsSuccArchimedean ι] noncomputable def orderIsoRangeToZOfLinearSuccPredArch [hι : Nonempty ι] : ι ≃o Set.range (toZ hι.some) where toEquiv := Equiv.ofInjective _ injective_toZ - map_rel_iff' := by intro i j; exact toZ_le_iff i j + map_rel_iff' := by simp instance (priority := 100) countable_of_linear_succ_pred_arch : Countable ι := by rcases isEmpty_or_nonempty ι with _ | hι @@ -398,7 +372,7 @@ noncomputable def orderIsoIntOfLinearSuccPredArch [NoMaxOrder ι] [NoMinOrder ι · simp_rw [if_neg (not_le.mpr hn)] rw [toZ_iterate_pred] simp only [hn.le, Int.toNat_of_nonneg, Int.neg_nonneg_of_nonpos, Int.neg_neg] - map_rel_iff' := by intro i j; exact toZ_le_iff i j + map_rel_iff' := by simp /-- If the order has a bot but no top, `toZ` defines an `OrderIso` between `ι` and `ℕ`. -/ def orderIsoNatOfLinearSuccPredArch [NoMaxOrder ι] [OrderBot ι] : ι ≃o ℕ where @@ -414,7 +388,7 @@ def orderIsoNatOfLinearSuccPredArch [NoMaxOrder ι] [OrderBot ι] : ι ≃o ℕ map_rel_iff' := by intro i j simp only [Equiv.coe_fn_mk, Int.toNat_le] - rw [← @toZ_le_iff ι _ _ _ _ ⊥, Int.toNat_of_nonneg (toZ_nonneg bot_le)] + rw [← toZ_le_toZ (i0 := (⊥ : ι)), Int.toNat_of_nonneg (toZ_nonneg bot_le)] /-- If the order has both a bot and a top, `toZ` gives an `OrderIso` between `ι` and `Finset.range n` for some `n`. -/ @@ -422,7 +396,7 @@ def orderIsoRangeOfLinearSuccPredArch [OrderBot ι] [OrderTop ι] : ι ≃o Finset.range ((toZ ⊥ (⊤ : ι)).toNat + 1) where toFun i := ⟨(toZ ⊥ i).toNat, - Finset.mem_range_succ_iff.mpr (Int.toNat_le_toNat ((toZ_le_iff _ _).mpr le_top))⟩ + Finset.mem_range_succ_iff.mpr (Int.toNat_le_toNat (toZ_le_toZ.mpr le_top))⟩ invFun n := succ^[n] ⊥ left_inv i := iterate_succ_toZ i bot_le right_inv n := by @@ -440,7 +414,7 @@ def orderIsoRangeOfLinearSuccPredArch [OrderBot ι] [OrderTop ι] : map_rel_iff' := by intro i j simp only [Equiv.coe_fn_mk, Subtype.mk_le_mk, Int.toNat_le] - rw [← @toZ_le_iff ι _ _ _ _ ⊥, Int.toNat_of_nonneg (toZ_nonneg bot_le)] + rw [← toZ_le_toZ (i0 := (⊥ : ι)), Int.toNat_of_nonneg (toZ_nonneg bot_le)] end OrderIso From 0b5b3a78acd5180ad4b683b83fe87738fc5ef1ff Mon Sep 17 00:00:00 2001 From: "Yury G. Kudryashov" <188813+urkud@users.noreply.github.com> Date: Sun, 10 May 2026 00:45:57 +0000 Subject: [PATCH 16/65] =?UTF-8?q?chore(*):=20golf=20while=20reducing=20def?= =?UTF-8?q?eq=20abuse=20`Set=20=CE=B1=20=3D=20=CE=B1=20=E2=86=92=20Prop`?= =?UTF-8?q?=20(#39020)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mathlib/Algebra/Algebra/Subalgebra/Lattice.lean | 12 ++---------- Mathlib/Algebra/Ring/Subring/MulOpposite.lean | 4 ++-- Mathlib/Analysis/Meromorphic/IsolatedZeros.lean | 10 +++------- Mathlib/Analysis/Polynomial/MahlerMeasure.lean | 2 +- .../Analysis/SpecialFunctions/Pow/Continuity.lean | 8 ++------ Mathlib/Data/Nat/Count.lean | 5 +---- Mathlib/Data/Set/Function.lean | 3 +++ 7 files changed, 14 insertions(+), 30 deletions(-) diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Lattice.lean b/Mathlib/Algebra/Algebra/Subalgebra/Lattice.lean index 451c38998d..5e4698cedb 100644 --- a/Mathlib/Algebra/Algebra/Subalgebra/Lattice.lean +++ b/Mathlib/Algebra/Algebra/Subalgebra/Lattice.lean @@ -700,19 +700,11 @@ theorem adjoin_span {s : Set A} : adjoin R (Submodule.span R s : Set A) = adjoin le_antisymm (adjoin_le (span_le_adjoin _ _)) (adjoin_mono Submodule.subset_span) theorem adjoin_image (f : A →ₐ[R] B) (s : Set A) : adjoin R (f '' s) = (adjoin R s).map f := - le_antisymm (adjoin_le <| Set.image_mono subset_adjoin) <| - Subalgebra.map_le.2 <| adjoin_le <| Set.image_subset_iff.1 <| by - simp only [Set.image_id', coe_carrier_toSubmonoid, Subalgebra.coe_toSubsemiring, - Subalgebra.coe_comap] - exact fun x hx => subset_adjoin ⟨x, hx, rfl⟩ + eq_of_forall_ge_iff fun t ↦ by simp [Subalgebra.map_le, adjoin_le_iff] @[simp] theorem adjoin_insert_adjoin (x : A) : adjoin R (insert x ↑(adjoin R s)) = adjoin R (insert x s) := - le_antisymm - (adjoin_le - (Set.insert_subset_iff.mpr - ⟨subset_adjoin (Set.mem_insert _ _), adjoin_mono (Set.subset_insert _ _)⟩)) - (Algebra.adjoin_mono (Set.insert_subset_insert Algebra.subset_adjoin)) + eq_of_forall_ge_iff fun t ↦ by simp [adjoin_le_iff, Set.insert_subset_iff] theorem mem_adjoin_of_map_mul {s} {x : A} {f : A →ₗ[R] B} (hf : ∀ a₁ a₂, f (a₁ * a₂) = f a₁ * f a₂) (h : x ∈ adjoin R s) : f x ∈ adjoin R (f '' (s ∪ {1})) := by diff --git a/Mathlib/Algebra/Ring/Subring/MulOpposite.lean b/Mathlib/Algebra/Ring/Subring/MulOpposite.lean index df84955c6f..f92b3d0ab5 100644 --- a/Mathlib/Algebra/Ring/Subring/MulOpposite.lean +++ b/Mathlib/Algebra/Ring/Subring/MulOpposite.lean @@ -26,7 +26,7 @@ variable {ι : Sort*} {R : Type*} [NonAssocRing R] @[simps! coe toSubsemiring] protected def op (S : Subring R) : Subring Rᵐᵒᵖ where toSubsemiring := S.toSubsemiring.op - neg_mem' {x} hx := neg_mem (show x.unop ∈ S from hx) + neg_mem' := by simp attribute [norm_cast] coe_op @@ -37,7 +37,7 @@ theorem mem_op {x : Rᵐᵒᵖ} {S : Subring R} : x ∈ S.op ↔ x.unop ∈ S := @[simps! coe toSubsemiring] protected def unop (S : Subring Rᵐᵒᵖ) : Subring R where toSubsemiring := S.toSubsemiring.unop - neg_mem' {x} hx := neg_mem (show MulOpposite.op x ∈ S from hx) + neg_mem' := by simp attribute [norm_cast] coe_unop diff --git a/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean b/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean index ba8b7c5d57..db23b63136 100644 --- a/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean +++ b/Mathlib/Analysis/Meromorphic/IsolatedZeros.lean @@ -60,13 +60,9 @@ theorem eventuallyEq_zero_nhdsNE_of_eventuallyEq_zero_codiscreteWithin (hf : Mer (h₁x : x ∈ U) (h₂x : AccPt x (𝓟 U)) (h : f =ᶠ[codiscreteWithin U] 0) : f =ᶠ[𝓝[≠] x] 0 := by rw [← hf.frequently_zero_iff_eventuallyEq_zero] - apply ((accPt_iff_frequently_nhdsNE.1 h₂x).and_eventually - (mem_codiscreteWithin_iff_forall_mem_nhdsNE.1 h x h₁x)).mp - filter_upwards - intro a - simp_rw [Pi.zero_apply] - rw [(by rfl : ({x | f x = 0} ∪ Uᶜ) a ↔ a ∈ {x | f x = 0} ∪ Uᶜ)] - simp_all + apply ((accPt_iff_frequently_nhdsNE.1 h₂x).and_eventually <| eventually_mem_set.2 + (mem_codiscreteWithin_iff_forall_mem_nhdsNE.1 h x h₁x)).mono + simp +contextual /-! ## Identity Principles diff --git a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean index 2f08b67f2d..4c67349ac2 100644 --- a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean +++ b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean @@ -319,7 +319,7 @@ theorem mahlerMeasure_le_sqrt_sum_sq_norm_coeff (p : Polynomial ℂ) : refine Finite.of_finite_image (f := circleMap 0 1) (p.roots.finite_toSet.subset ?_) ?_ · rintro z ⟨θ, ⟨_, heval⟩, rfl⟩ exact (mem_roots hp).mpr heval - · apply InjOn.mono fun _ h ↦ h.1 + · grw [setOf_and, inter_subset_left] exact injOn_circleMap_of_abs_sub_le one_ne_zero (by simp [abs_of_pos pi_pos]) have hlogAe : ∀ᵐ (θ : ℝ) ∂volume.restrict (uIoc 0 (2 * π)), exp (log ‖p.eval (circleMap 0 1 θ)‖) = ‖p.eval (circleMap 0 1 θ)‖ := by diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean b/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean index e50ab60658..ee111a114b 100644 --- a/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean +++ b/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean @@ -49,12 +49,8 @@ theorem cpow_eq_nhds {a b : ℂ} (ha : a ≠ 0) : theorem cpow_eq_nhds' {p : ℂ × ℂ} (hp_fst : p.fst ≠ 0) : (fun x => x.1 ^ x.2) =ᶠ[𝓝 p] fun x => exp (log x.1 * x.2) := by - suffices ∀ᶠ x : ℂ × ℂ in 𝓝 p, x.1 ≠ 0 from - this.mono fun x hx ↦ by - dsimp only - rw [cpow_def_of_ne_zero hx] - refine IsOpen.eventually_mem ?_ hp_fst - change IsOpen { x : ℂ × ℂ | x.1 = 0 }ᶜ + suffices IsOpen {x : ℂ × ℂ | x.1 = 0}ᶜ from + mem_nhds_iff.mpr ⟨_, fun x hx ↦ by simp_all [cpow_def_of_ne_zero], this, hp_fst⟩ rw [isOpen_compl_iff] exact isClosed_eq continuous_fst continuous_const diff --git a/Mathlib/Data/Nat/Count.lean b/Mathlib/Data/Nat/Count.lean index 929beea262..3e7c42de14 100644 --- a/Mathlib/Data/Nat/Count.lean +++ b/Mathlib/Data/Nat/Count.lean @@ -71,10 +71,7 @@ theorem count_monotone : Monotone (count p) := monotone_nat_of_le_succ (by grind) theorem count_add (a b : ℕ) : count p (a + b) = count p a + count (fun k ↦ p (a + k)) b := by - have : Disjoint {x ∈ range a | p x} {x ∈ (range b).map <| addLeftEmbedding a | p x} := by - grind [Finset.disjoint_left] - simp_rw [count_eq_card_filter_range, range_add, filter_union, card_union_of_disjoint this, - filter_map, addLeftEmbedding, card_map, Function.Embedding.coeFn_mk, Function.comp_def] + simp [count, List.range_add, Function.comp_def] theorem count_add' (a b : ℕ) : count p (a + b) = count (fun k ↦ p (k + b)) a + count p b := by rw [add_comm, count_add, add_comm] diff --git a/Mathlib/Data/Set/Function.lean b/Mathlib/Data/Set/Function.lean index 89c6138a67..bbc6970df9 100644 --- a/Mathlib/Data/Set/Function.lean +++ b/Mathlib/Data/Set/Function.lean @@ -167,6 +167,7 @@ lemma mapsTo_of_subsingleton' [Subsingleton β] (f : α → β) (h : s.Nonempty lemma mapsTo_of_subsingleton [Subsingleton α] (f : α → α) (s : Set α) : MapsTo f s s := mapsTo_of_subsingleton' _ id +@[gcongr] theorem MapsTo.mono (hf : MapsTo f s₁ t₁) (hs : s₂ ⊆ s₁) (ht : t₁ ⊆ t₂) : MapsTo f s₂ t₂ := fun _ hx => ht (hf <| hs hx) @@ -270,6 +271,7 @@ theorem InjOn.congr (h₁ : InjOn f₁ s) (h : EqOn f₁ f₂ s) : InjOn f₂ s theorem EqOn.injOn_iff (H : EqOn f₁ f₂ s) : InjOn f₁ s ↔ InjOn f₂ s := ⟨fun h => h.congr H, fun h => h.congr H.symm⟩ +@[gcongr] theorem InjOn.mono (h : s₁ ⊆ s₂) (ht : InjOn f s₂) : InjOn f s₁ := fun _ hx _ hy H => ht (h hx) (h hy) H @@ -485,6 +487,7 @@ theorem SurjOn.congr (h : SurjOn f₁ s t) (H : EqOn f₁ f₂ s) : SurjOn f₂ theorem EqOn.surjOn_iff (h : EqOn f₁ f₂ s) : SurjOn f₁ s t ↔ SurjOn f₂ s t := ⟨fun H => H.congr h, fun H => H.congr h.symm⟩ +@[gcongr] theorem SurjOn.mono (hs : s₁ ⊆ s₂) (ht : t₁ ⊆ t₂) (hf : SurjOn f s₁ t₂) : SurjOn f s₂ t₁ := Subset.trans ht <| Subset.trans hf <| image_mono hs From fbe99d1273f63b5085c0a654ba1f5aeae7d5beb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 00:45:59 +0000 Subject: [PATCH 17/65] feat(Algebra/Order/SuccPred): generalize `CanonicallyOrderedAdd` to `IsBotZeroClass` (#39062) To be used in the CGT repo. --- Mathlib/Algebra/Order/IsBotOne.lean | 2 +- Mathlib/Algebra/Order/SuccPred.lean | 30 ++++++++++++++--------------- Mathlib/Order/SuccPred/Limit.lean | 7 +++++++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Mathlib/Algebra/Order/IsBotOne.lean b/Mathlib/Algebra/Order/IsBotOne.lean index 6396efb92d..8ddae9cfba 100644 --- a/Mathlib/Algebra/Order/IsBotOne.lean +++ b/Mathlib/Algebra/Order/IsBotOne.lean @@ -45,7 +45,7 @@ alias zero_le' := zero_le variable (α) in /-- Create an `OrderBot` instance, setting `1` as the bottom element. -/ -@[to_additive (attr := implicit_reducible) +@[expose, to_additive (attr := implicit_reducible) /-- Create an `OrderBot` instance, setting `0` as the bottom element. -/] def IsBotOneClass.toOrderBot : OrderBot α where bot := 1 diff --git a/Mathlib/Algebra/Order/SuccPred.lean b/Mathlib/Algebra/Order/SuccPred.lean index 86a9441c1c..cea3e544d7 100644 --- a/Mathlib/Algebra/Order/SuccPred.lean +++ b/Mathlib/Algebra/Order/SuccPred.lean @@ -132,8 +132,8 @@ theorem one_le_iff_pos [AddMonoidWithOne α] [ZeroLEOneClass α] [NeZero (1 : α [SuccAddOrder α] : 1 ≤ x ↔ 0 < x := by rw [← succ_le_iff_of_not_isMax not_isMax_zero, succ_eq_add_one, zero_add] -theorem one_le_iff_ne_zero [AddMonoidWithOne α] [ZeroLEOneClass α] [NeZero (1 : α)] - [SuccAddOrder α] [CanonicallyOrderedAdd α] : 1 ≤ x ↔ x ≠ 0 := by +theorem one_le_iff_ne_zero [AddMonoidWithOne α] [NeZero (1 : α)] + [SuccAddOrder α] [IsBotZeroClass α] : 1 ≤ x ↔ x ≠ 0 := by rw [Order.one_le_iff_pos, pos_iff_ne_zero] theorem covBy_iff_add_one_eq [Add α] [One α] [SuccAddOrder α] [NoMaxOrder α] : @@ -186,14 +186,12 @@ theorem IsPredLimit.lt_sub_natCast [AddCommGroupWithOne α] [PredSubOrder α] (hx : IsPredLimit x) (hy : x < y) : ∀ n : ℕ, x < y - n := hx.isPredPrelimit.lt_sub_natCast hy -theorem IsSuccLimit.natCast_lt [AddMonoidWithOne α] [SuccAddOrder α] - [OrderBot α] [CanonicallyOrderedAdd α] +theorem IsSuccLimit.natCast_lt [AddMonoidWithOne α] [SuccAddOrder α] [IsBotZeroClass α] (hx : IsSuccLimit x) : ∀ n : ℕ, n < x := by - simpa [bot_eq_zero] using hx.add_natCast_lt hx.bot_lt + simpa using hx.add_natCast_lt hx.pos -theorem not_isSuccLimit_natCast [AddMonoidWithOne α] [SuccAddOrder α] - [OrderBot α] [CanonicallyOrderedAdd α] - (n : ℕ) : ¬ IsSuccLimit (n : α) := +theorem not_isSuccLimit_natCast [AddMonoidWithOne α] [SuccAddOrder α] [IsBotZeroClass α] (n : ℕ) : + ¬ IsSuccLimit (n : α) := fun h ↦ (h.natCast_lt n).false @[simp] @@ -207,7 +205,7 @@ theorem not_isSuccLimit_add_one (a : α) [Add α] [One α] [SuccAddOrder α] [No succ_eq_add_one a ▸ not_isSuccLimit_succ a @[simp] -theorem succ_eq_zero [AddZeroClass α] [OrderBot α] [CanonicallyOrderedAdd α] [One α] [NoMaxOrder α] +theorem succ_eq_zero [AddZeroClass α] [OrderBot α] [IsBotZeroClass α] [One α] [NoMaxOrder α] [SuccAddOrder α] {a : WithBot α} : WithBot.succ a = 0 ↔ a = ⊥ := by cases a · simp [bot_eq_zero] @@ -247,30 +245,30 @@ section AddMonoidWithOne variable [AddMonoidWithOne α] [NoMaxOrder α] [SuccAddOrder α] @[simp] -theorem lt_one_iff [CanonicallyOrderedAdd α] : x < 1 ↔ x = 0 := by +theorem lt_one_iff [IsBotZeroClass α] : x < 1 ↔ x = 0 := by rw [← zero_add 1, lt_add_one_iff, nonpos_iff_eq_zero] -theorem le_one_iff [CanonicallyOrderedAdd α] : x ≤ 1 ↔ x = 0 ∨ x = 1 := by +theorem le_one_iff [IsBotZeroClass α] : x ≤ 1 ↔ x = 0 ∨ x = 1 := by rw [le_iff_lt_or_eq, lt_one_iff] @[simp] -theorem Iio_one [CanonicallyOrderedAdd α] : Set.Iio (1 : α) = {0} := by +theorem Iio_one [IsBotZeroClass α] : Set.Iio (1 : α) = {0} := by ext; simp -theorem Iic_one [CanonicallyOrderedAdd α] : Set.Iic (1 : α) = {0, 1} := by +theorem Iic_one [IsBotZeroClass α] : Set.Iic (1 : α) = {0, 1} := by ext; simp [le_one_iff] @[simp] theorem lt_two_iff : x < 2 ↔ x ≤ 1 := by rw [← one_add_one_eq_two, lt_add_one_iff] -theorem le_two_iff [CanonicallyOrderedAdd α] : x ≤ 2 ↔ x = 0 ∨ x = 1 ∨ x = 2 := by +theorem le_two_iff [IsBotZeroClass α] : x ≤ 2 ↔ x = 0 ∨ x = 1 ∨ x = 2 := by rw [le_iff_lt_or_eq, lt_two_iff, le_one_iff, or_assoc] -theorem Iio_two [CanonicallyOrderedAdd α] : Set.Iio (2 : α) = {0, 1} := by +theorem Iio_two [IsBotZeroClass α] : Set.Iio (2 : α) = {0, 1} := by ext; simp [le_one_iff] -theorem Iic_two [CanonicallyOrderedAdd α] : Set.Iic (2 : α) = {0, 1, 2} := by +theorem Iic_two [IsBotZeroClass α] : Set.Iic (2 : α) = {0, 1, 2} := by ext; simp [le_two_iff] end AddMonoidWithOne diff --git a/Mathlib/Order/SuccPred/Limit.lean b/Mathlib/Order/SuccPred/Limit.lean index db9acec0c4..b39c005fbd 100644 --- a/Mathlib/Order/SuccPred/Limit.lean +++ b/Mathlib/Order/SuccPred/Limit.lean @@ -164,6 +164,13 @@ theorem IsSuccLimit.bot_lt [OrderBot α] (h : IsSuccLimit a) : ⊥ < a := theorem IsSuccLimit.ne_bot [OrderBot α] (h : IsSuccLimit a) : a ≠ ⊥ := h.bot_lt.ne' +theorem IsSuccLimit.pos [Zero α] [IsBotZeroClass α] (h : IsSuccLimit a) : 0 < a := + let := IsBotZeroClass.toOrderBot α + h.bot_lt + +theorem IsSuccLimit.ne_zero [Zero α] [IsBotZeroClass α] (h : IsSuccLimit a) : a ≠ 0 := + h.pos.ne' + @[to_dual] theorem not_isSuccLimit_iff : ¬ IsSuccLimit a ↔ IsMin a ∨ ¬ IsSuccPrelimit a := by rw [IsSuccLimit, not_and_or, not_not] From 10647b5fa342f274f205b94468b125277a1aa351 Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:23 +0000 Subject: [PATCH 18/65] feat(Data/List/TakeDrop): `take`/`drop` + `tail`/`dropLast` almost commute (#35632) RFC to lean4: https://github.com/leanprover/lean4/issues/12910 [Zulip](https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/List.2Etail.2FList.2EdropLast.20.2B.20List.2Etake/with/572294698) --- Mathlib/Data/List/TakeDrop.lean | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Mathlib/Data/List/TakeDrop.lean b/Mathlib/Data/List/TakeDrop.lean index a297cc1411..897b733c06 100644 --- a/Mathlib/Data/List/TakeDrop.lean +++ b/Mathlib/Data/List/TakeDrop.lean @@ -88,6 +88,28 @@ theorem tail_iterate (l : List α) (n : ℕ) : (List.tail^[n]) l = l.drop n := b | zero => rfl | succ n ih => cases l <;> simp [*] +section TailDropLast + +variable (l : List α) (n : ℕ) + +theorem tail_take_eq_take_tail : (l.take n).tail = l.tail.take (n - 1) := by + ext + grind + +theorem dropLast_take_eq_take_dropLast : (l.take n).dropLast = l.dropLast.take (n - 1) := by + ext + grind + +theorem tail_drop_eq_drop_tail : (l.drop n).tail = l.tail.drop n := by + ext + grind + +theorem dropLast_drop_eq_drop_dropLast : (l.drop n).dropLast = l.dropLast.drop n := by + ext + grind + +end TailDropLast + section TakeI variable [Inhabited α] From 1bfec08c333f3d9f55366b877673f8b15f2a2fb5 Mon Sep 17 00:00:00 2001 From: Snir Broshi <26556598+SnirBroshi@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:25 +0000 Subject: [PATCH 19/65] chore(Data/Int/Init): generalize `le_induction` from `Prop` to `Sort*` + def lemmas (#37171) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Generallise `le_induction` from `Prop` to `Sort*` and rename to `leInduction` - Add a few lemmas - Simplify proofs using `lia` - Move `inductionOn'_add_one` [Zulip 💬](https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Add.20note.20to.20help.20search.20similar.20thms/near/535331432) --- Mathlib/Data/Int/Basic.lean | 19 -------- Mathlib/Data/Int/Init.lean | 81 +++++++++++++++++++++----------- Mathlib/GroupTheory/CoprodI.lean | 4 +- 3 files changed, 56 insertions(+), 48 deletions(-) diff --git a/Mathlib/Data/Int/Basic.lean b/Mathlib/Data/Int/Basic.lean index 0e3d1925f9..fe3824cdc1 100644 --- a/Mathlib/Data/Int/Basic.lean +++ b/Mathlib/Data/Int/Basic.lean @@ -33,25 +33,6 @@ instance instNontrivial : Nontrivial ℤ := ⟨⟨0, 1, Int.zero_ne_one⟩⟩ @[simp] lemma ofNat_injective : Function.Injective ofNat := @Int.ofNat.inj -section inductionOn' - -variable {C : ℤ → Sort*} (z b : ℤ) - (H0 : C b) (Hs : ∀ k, b ≤ k → C k → C (k + 1)) (Hp : ∀ k ≤ b, C k → C (k - 1)) - -variable {z b H0 Hs Hp} - -lemma inductionOn'_add_one (hz : b ≤ z) : - (z + 1).inductionOn' b H0 Hs Hp = Hs z hz (z.inductionOn' b H0 Hs Hp) := by - apply cast_eq_iff_heq.mpr - lift z - b to ℕ using Int.sub_nonneg.mpr hz with zb hzb - rw [show z + 1 - b = zb + 1 by lia] - have : b + zb = z := by lia - subst this - convert cast_heq _ _ - rw [Int.inductionOn', cast_eq_iff_heq, ← hzb] - -end inductionOn' - section strongRec variable {P : ℤ → Sort*} {lt : ∀ n < m, P n} {ge : ∀ n ≥ m, (∀ k < n, P k) → P n} diff --git a/Mathlib/Data/Int/Init.lean b/Mathlib/Data/Int/Init.lean index 9ad427ad2b..d06ffbf364 100644 --- a/Mathlib/Data/Int/Init.lean +++ b/Mathlib/Data/Int/Init.lean @@ -8,6 +8,7 @@ module public import Batteries.Logic public import Mathlib.Data.Int.Notation public import Mathlib.Data.Nat.Notation +public import Mathlib.Tactic.DepRewrite /-! # Basic operations on the integers @@ -92,7 +93,7 @@ variable {motive : ℤ → Sort*} (z b : ℤ) (zero : motive b) /-- Inductively define a function on `ℤ` by defining it at `b`, for the `succ` of a number greater than `b`, and the `pred` of a number less than `b`. -/ @[elab_as_elim] protected def inductionOn' : motive z := - cast (congrArg motive <| show b + (z - b) = z by rw [Int.add_comm, z.sub_add_cancel b]) <| + cast (congrArg motive <| show b + (z - b) = z by lia) <| match z - b with | .ofNat n => pos n | .negSucc n => neg n @@ -100,35 +101,32 @@ where /-- The positive case of `Int.inductionOn'`. -/ pos : ∀ n : ℕ, motive (b + n) | 0 => cast (by simp) zero - | n + 1 => cast (by rw [Int.add_assoc]; rfl) <| - succ _ (Int.le_add_of_nonneg_right (natCast_nonneg _)) (pos n) + | n + 1 => cast (by lia) <| succ _ (Int.le_add_of_nonneg_right (natCast_nonneg _)) (pos n) /-- The negative case of `Int.inductionOn'`. -/ neg : ∀ n : ℕ, motive (b + -[n+1]) | 0 => pred _ Int.le_rfl zero - | n + 1 => by - refine cast (by lia) (pred _ (Int.le_of_lt ?_) (neg n)) - lia + | n + 1 => cast (by lia) <| pred _ (by lia) (neg n) variable {z b zero succ pred} lemma inductionOn'_self : b.inductionOn' b zero succ pred = zero := cast_eq_iff_heq.mpr <| .symm <| by rw [b.sub_self, ← cast_eq_iff_heq]; rfl -lemma inductionOn'_sub_one (hz : z ≤ b) : +theorem inductionOn'_add_one (hz : b ≤ z) : + (z + 1).inductionOn' b zero succ pred = succ z hz (z.inductionOn' b zero succ pred) := by + unfold Int.inductionOn' + rw! [show z - b = (z - b).toNat by lia, show z + 1 - b = ((z - b).toNat + 1 : ℕ) by lia] + grind [inductionOn'.pos, show b + (z - b).toNat = z by lia] + +theorem inductionOn'_sub_one (hz : z ≤ b) : (z - 1).inductionOn' b zero succ pred = pred z hz (z.inductionOn' b zero succ pred) := by - apply cast_eq_iff_heq.mpr - obtain ⟨n, hn⟩ := Int.eq_negSucc_of_lt_zero (show z - 1 - b < 0 by lia) - rw [hn] - obtain _ | n := n - · change _ = -1 at hn - have : z = b := by lia - subst this; rw [inductionOn'_self]; exact heq_of_eq rfl - · have : z = b + -[n+1] := by rw [Int.negSucc_eq] at hn ⊢; lia - subst this - refine (cast_heq _ _).trans ?_ - congr - symm - rw [Int.inductionOn', cast_eq_iff_heq, show b + -[n+1] - b = -[n+1] by lia] + unfold Int.inductionOn' + conv => lhs; unfold inductionOn'.neg + by_cases z = b + · rw! [show z - 1 - b = -[(b - z).toNat+1] by lia, show z - b = 0 by lia] + grind [inductionOn'.pos] + rw! [show z - 1 - b = -[(b - z).toNat+1] by lia, show z - b = -[(b - z - 1).toNat+1] by lia] + grind end inductionOn' @@ -140,16 +138,45 @@ end inductionOn' /-- See `Int.inductionOn'` for an induction in both directions. -/ @[elab_as_elim] -protected lemma le_induction {m : ℤ} {motive : ∀ n, m ≤ n → Prop} (base : motive m m.le_refl) - (succ : ∀ n hmn, motive n hmn → motive (n + 1) (le_add_one hmn)) : ∀ n hmn, motive n hmn := by - refine fun n ↦ Int.inductionOn' n m ?_ ?_ ?_ <;> grind +protected def leInduction {m : ℤ} {motive : ∀ n, m ≤ n → Sort*} (base : motive m m.le_refl) + (succ : ∀ n hmn, motive n hmn → motive (n + 1) (le_add_one hmn)) : ∀ n hmn, motive n hmn := + fun n ↦ n.inductionOn' m + (fun _ ↦ base) (fun k hle ih _ ↦ succ k hle <| ih hle) (fun _ _ _ _ ↦ False.elim <| by lia) + +@[deprecated (since := "2026-03-25")] protected alias le_induction := Int.leInduction + +theorem leInduction_base {m : ℤ} {motive : ∀ n, m ≤ n → Sort*} (base : motive m m.le_refl) + (succ : ∀ n hmn, motive n hmn → motive (n + 1) (le_add_one hmn)) : + Int.leInduction (motive := motive) base succ m m.le_refl = base := by + rw [Int.leInduction, inductionOn'_self] + +theorem leInduction_add_one {m : ℤ} {motive : ∀ n, m ≤ n → Sort*} (base : motive m m.le_refl) + (succ : ∀ n hmn, motive n hmn → motive (n + 1) (le_add_one hmn)) (n : ℤ) (hmn : m ≤ n) : + Int.leInduction (motive := motive) base succ (n + 1) (by lia) = + succ n hmn (Int.leInduction (motive := motive) base succ n hmn) := by + rw [Int.leInduction, inductionOn'_add_one hmn] + rfl /-- See `Int.inductionOn'` for an induction in both directions. -/ @[elab_as_elim] -protected lemma le_induction_down {m : ℤ} {motive : ∀ n, n ≤ m → Prop} (base : motive m m.le_refl) - (pred : ∀ n hmn, motive n hmn → motive (n - 1) (by lia)) : ∀ n hmn, motive n hmn := fun n ↦ - Int.inductionOn' n m (fun _ ↦ base) (fun k hle _ hle' ↦ by lia) - fun k hle hi _ ↦ pred k hle (hi hle) +protected def leInductionDown {m : ℤ} {motive : ∀ n, n ≤ m → Sort*} (base : motive m m.le_refl) + (pred : ∀ n hnm, motive n hnm → motive (n - 1) (by lia)) : ∀ n hnm, motive n hnm := + fun n ↦ n.inductionOn' m + (fun _ ↦ base) (fun _ _ _ _ ↦ False.elim <| by lia) (fun k hle ih _ ↦ pred k hle <| ih hle) + +theorem leInductionDown_base {m : ℤ} {motive : ∀ n, n ≤ m → Sort*} (base : motive m m.le_refl) + (pred : ∀ n hnm, motive n hnm → motive (n - 1) (by lia)) : + Int.leInductionDown (motive := motive) base pred m m.le_refl = base := by + rw [Int.leInductionDown, inductionOn'_self] + +theorem leInductionDown_sub_one {m : ℤ} {motive : ∀ n, n ≤ m → Sort*} (base : motive m m.le_refl) + (pred : ∀ n hnm, motive n hnm → motive (n - 1) (by lia)) (n : ℤ) (hnm : n ≤ m) : + Int.leInductionDown (motive := motive) base pred (n - 1) (by lia) = + pred n hnm (Int.leInductionDown (motive := motive) base pred n hnm) := by + rw [Int.leInductionDown, inductionOn'_sub_one hnm] + rfl + +@[deprecated (since := "2026-03-25")] protected alias le_induction_down := Int.leInductionDown section strongRec diff --git a/Mathlib/GroupTheory/CoprodI.lean b/Mathlib/GroupTheory/CoprodI.lean index 13b16cd3e4..fb66081c6a 100644 --- a/Mathlib/GroupTheory/CoprodI.lean +++ b/Mathlib/GroupTheory/CoprodI.lean @@ -1017,7 +1017,7 @@ theorem _root_.FreeGroup.injective_lift_of_ping_pong : Function.Injective (FreeG smul_set_mono ((hXYdisj j i).union_left <| hYdisj hij.symm).subset_compl_right _ ⊆ X i := by clear hnne0 hlt - induction n, h1n using Int.le_induction with + induction n, h1n using Int.leInduction with | base => rw [zpow_one]; exact hX i | succ n _hle hi => calc @@ -1035,7 +1035,7 @@ theorem _root_.FreeGroup.injective_lift_of_ping_pong : Function.Injective (FreeG smul_set_mono ((hXdisj hij.symm).union_left (hXYdisj i j).symm).subset_compl_right _ ⊆ Y i := by clear hnne0 hgt - induction n, h1n using Int.le_induction_down with + induction n, h1n using Int.leInductionDown with | base => rw [zpow_neg, zpow_one]; exact hY i | pred n hle hi => calc From bcfd9817b0d76b790850860e3ad947cd3ea64d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Renison?= <85908989+IvanRenison@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:27 +0000 Subject: [PATCH 20/65] feat(Data/List): add lemma `List.notMem_subset` (#37561) Zulip question: https://leanprover.zulipchat.com/#narrow/channel/217875-Is-there-code-for-X.3F/topic/List.2EnotMem_subset/with/583146215 --- Mathlib/Data/List/Basic.lean | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Mathlib/Data/List/Basic.lean b/Mathlib/Data/List/Basic.lean index 576b199501..433d9cb936 100644 --- a/Mathlib/Data/List/Basic.lean +++ b/Mathlib/Data/List/Basic.lean @@ -186,6 +186,8 @@ theorem map_subset_iff {l₁ l₂ : List α} (f : α → β) (h : Injective f) : rcases mem_map.1 (h2 (mem_map_of_mem hx)) with ⟨x', hx', hxx'⟩ cases h hxx'; exact hx' +lemma notMem_of_subset (h : l ⊆ l₁) {a : α} (ha : a ∉ l₁) : a ∉ l := (ha <| h ·) + /-! ### append -/ theorem append_eq_has_append {L₁ L₂ : List α} : List.append L₁ L₂ = L₁ ++ L₂ := From 9e7d86bee87c5607e3453747eb977925cb7e84bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Renison?= <85908989+IvanRenison@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:30 +0000 Subject: [PATCH 21/65] feat(Data/Finset/Preimage): add lemmas about `Equiv.symm` (#37676) `Finset` version of lemmas already existing for `Set`. --- Mathlib/Data/Finset/Preimage.lean | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Mathlib/Data/Finset/Preimage.lean b/Mathlib/Data/Finset/Preimage.lean index ff7918d1c9..d1a8a1b76b 100644 --- a/Mathlib/Data/Finset/Preimage.lean +++ b/Mathlib/Data/Finset/Preimage.lean @@ -191,6 +191,14 @@ def restrictPreimageFinset (e : α ≃ β) (s : Finset β) : (s.preimage e e.inj left_inv _ := by simp right_inv _ := by simp +lemma image_symm_eq_preimage_of_finset [DecidableEq α] (e : α ≃ β) (s : Finset β) : + s.image e.symm = s.preimage e e.injective.injOn := by + grind [Finset.mem_preimage] + +lemma image_eq_preimage_symm_of_finset [DecidableEq β] (e : α ≃ β) (s : Finset α) : + s.image e = s.preimage e.symm e.symm.injective.injOn := + e.symm.image_symm_eq_preimage_of_finset s + end Equiv set_option backward.isDefEq.respectTransparency false in From 7c804503037427d17313401ca764f7741eb85439 Mon Sep 17 00:00:00 2001 From: FordUniver <61389961+FordUniver@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:32 +0000 Subject: [PATCH 22/65] feat(Data/Finset/Powerset): add `filter_powersetCard_subset` (#38370) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `Finset.filter_powersetCard_subset` and derives `Finset.card_filter_powersetCard_subset` from it. The hypothesis `s.card ≤ n` is necessary: without it, natural subtraction silently gives `Nat.choose k 0 = 1 ≠ 0`. Co-authored-by: Malte Jackisch Co-authored-by: Christoph Spiegel --- Mathlib/Data/Finset/Powerset.lean | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Mathlib/Data/Finset/Powerset.lean b/Mathlib/Data/Finset/Powerset.lean index d37121600e..2a9df10393 100644 --- a/Mathlib/Data/Finset/Powerset.lean +++ b/Mathlib/Data/Finset/Powerset.lean @@ -201,6 +201,36 @@ theorem card_powersetCard (n : ℕ) (s : Finset α) : card (powersetCard n s) = Nat.choose (card s) n := (card_pmap _ _ _).trans (Multiset.card_powersetCard n s.1) +/-- The `n`-element subsets of `t` containing `s` are exactly the `(n - s.card)`-element +subsets of `t \ s`, unioned with `s`. -/ +theorem filter_powersetCard_subset [DecidableEq α] (s t : Finset α) (n : ℕ) + (hst : s ⊆ t) (hsn : #s ≤ n) : + (t.powersetCard n).filter (s ⊆ ·) = ((t \ s).powersetCard (n - #s)).image (· ∪ s) := by + ext x + simp only [mem_filter, mem_powersetCard, mem_image] + constructor + · intro ⟨⟨hxt, hxn⟩, hsx⟩ + exact ⟨x \ s, ⟨fun y hy => mem_sdiff.mpr ⟨hxt (mem_sdiff.mp hy).1, (mem_sdiff.mp hy).2⟩, + by rw [card_sdiff_of_subset hsx, hxn]⟩, sdiff_union_of_subset hsx⟩ + · rintro ⟨y, ⟨hyt, hyn⟩, rfl⟩ + refine ⟨⟨union_subset (hyt.trans sdiff_subset) hst, ?_⟩, subset_union_right⟩ + rw [card_union_of_disjoint (disjoint_of_subset_left hyt disjoint_sdiff_self_left), hyn] + omega + +/-- The number of `n`-element subsets of `t` containing `s` equals +`Nat.choose (#t - #s) (n - #s)`. -/ +lemma card_filter_powersetCard_subset [DecidableEq α] (s t : Finset α) (n : ℕ) + (hst : s ⊆ t) (hsn : #s ≤ n) : + #((t.powersetCard n).filter (s ⊆ ·)) = Nat.choose (#t - #s) (n - #s) := by + have hinj : Set.InjOn (· ∪ s) ↑((t \ s).powersetCard (n - #s)) := fun a ha b hb hab => + (union_sdiff_cancel_right + (disjoint_of_subset_left (mem_powersetCard.mp ha).1 disjoint_sdiff_self_left)).symm.trans + ((congrArg (· \ s) hab).trans + (union_sdiff_cancel_right + (disjoint_of_subset_left (mem_powersetCard.mp hb).1 disjoint_sdiff_self_left))) + simp only [filter_powersetCard_subset s t n hst hsn, card_image_of_injOn hinj, + card_powersetCard, card_sdiff_of_subset hst] + @[simp] theorem powersetCard_zero (s : Finset α) : s.powersetCard 0 = {∅} := by grind From 84fbb3671d0baa69feac0e182cc682ec3898ecb6 Mon Sep 17 00:00:00 2001 From: Christian Merten <136261474+chrisflav@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:34 +0000 Subject: [PATCH 23/65] chore(RingTheory): move `IsLocalizedModule.Away` to the `Basic` file and use it everywhere (#38414) --- .../Algebra/Module/FinitePresentation.lean | 8 +++--- .../Algebra/Module/LocalizedModule/Away.lean | 25 ++----------------- .../Algebra/Module/LocalizedModule/Basic.lean | 16 ++++++++++++ Mathlib/AlgebraicGeometry/Modules/Tilde.lean | 2 +- Mathlib/AlgebraicGeometry/StructureSheaf.lean | 6 ++--- .../RingTheory/LocalProperties/Exactness.lean | 1 - .../RingTheory/LocalProperties/Submodule.lean | 2 +- .../RingTheory/Localization/Finiteness.lean | 14 +++++------ Mathlib/RingTheory/Localization/Free.lean | 6 ++--- .../RingTheory/Spectrum/Prime/FreeLocus.lean | 2 +- Mathlib/RingTheory/Spectrum/Prime/Module.lean | 2 +- Mathlib/RingTheory/Support.lean | 6 ++--- 12 files changed, 42 insertions(+), 48 deletions(-) diff --git a/Mathlib/Algebra/Module/FinitePresentation.lean b/Mathlib/Algebra/Module/FinitePresentation.lean index 36e3c5985d..ba90a5cd24 100644 --- a/Mathlib/Algebra/Module/FinitePresentation.lean +++ b/Mathlib/Algebra/Module/FinitePresentation.lean @@ -475,7 +475,7 @@ lemma exists_bijective_map_powers [Module.Finite R M] [Module.FinitePresentation ext x have : s₁.1 • l (l' x) = s₁.1 • s₀.1 • x := congr($hs₁ x) simp [lₛ, lₛ', LocalizedModule.smul'_mk, this] - let eₛ : LocalizedModule (.powers t) M ≃ₗ[Rₛ] LocalizedModule (.powers t) N := + let eₛ : LocalizedModule.Away t M ≃ₗ[Rₛ] LocalizedModule.Away t N := { __ := lₛ, invFun := ((hu₀.unit⁻¹).1 • lₛ'), left_inv := fun x ↦ congr($H_left x), @@ -505,8 +505,8 @@ lemma Module.FinitePresentation.exists_lift_equiv_of_isLocalizedModule [Module.FinitePresentation R M] [Module.FinitePresentation R N] (l : M' ≃ₗ[R] N') : ∃ (r : R) (hr : r ∈ S) - (l' : LocalizedModule (.powers r) M ≃ₗ[Localization (.powers r)] - LocalizedModule (.powers r) N), + (l' : LocalizedModule.Away r M ≃ₗ[Localization (.powers r)] + LocalizedModule.Away r N), (LocalizedModule.lift (.powers r) g fun s ↦ map_units g ⟨s.1, SetLike.le_def.mp (Submonoid.powers_le.mpr hr) s.2⟩) ∘ₗ l'.toLinearMap = l ∘ₗ (LocalizedModule.lift (.powers r) f fun s ↦ map_units f ⟨s.1, SetLike.le_def.mp @@ -581,7 +581,7 @@ instance [Module.FinitePresentation R M] : is finitely presented, then `Mₛ = M[1/r]` for some `r ∈ S`. -/ lemma IsLocalizedModule.exists_isLocalizedModule_powers_of_finitePresentation [Module.Finite R M] [Module.FinitePresentation R M'] : - ∃ r ∈ S, IsLocalizedModule (.powers r) f := by + ∃ r ∈ S, IsLocalizedModule.Away r f := by have : IsLocalizedModule S (.id (R := R) (M := M')) := ⟨IsLocalizedModule.map_units f, fun y ↦ ⟨⟨y, 1⟩, by simp⟩, by simpa using ⟨1, S.one_mem⟩⟩ obtain ⟨r, hrp, H⟩ := exists_bijective_map_powers S diff --git a/Mathlib/Algebra/Module/LocalizedModule/Away.lean b/Mathlib/Algebra/Module/LocalizedModule/Away.lean index 9fbe7c96d9..1513c0b071 100644 --- a/Mathlib/Algebra/Module/LocalizedModule/Away.lean +++ b/Mathlib/Algebra/Module/LocalizedModule/Away.lean @@ -1,26 +1,5 @@ -/- -Copyright (c) 2025 Yongle Hu. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Yongle Hu --/ -module +module -- shake: keep-all public import Mathlib.Algebra.Module.LocalizedModule.Basic -/-! -# Localizations of modules away from an element --/ - -public section - -/-- Given `x : R` and `f : M →ₗ[R] M'`, `IsLocalization.Away x f` states that `M'` - is isomorphic to the localization of `M` at the submonoid generated by `x`. -/ -protected abbrev IsLocalizedModule.Away {R M M' : Type*} [CommSemiring R] (x : R) [AddCommMonoid M] - [Module R M] [AddCommMonoid M'] [Module R M'] (f : M →ₗ[R] M') := - IsLocalizedModule (Submonoid.powers x) f - -/-- Given `x : R`, `LocalizedModule.Away x M` is the localization of `M` at the - submonoid generated by `x`. -/ -protected abbrev LocalizedModule.Away {R : Type*} [CommSemiring R] (x : R) - (M : Type*) [AddCommMonoid M] [Module R M] := - LocalizedModule (Submonoid.powers x) M +deprecated_module (since := "2026-04-23") diff --git a/Mathlib/Algebra/Module/LocalizedModule/Basic.lean b/Mathlib/Algebra/Module/LocalizedModule/Basic.lean index 8ba95aca42..07dd45e351 100644 --- a/Mathlib/Algebra/Module/LocalizedModule/Basic.lean +++ b/Mathlib/Algebra/Module/LocalizedModule/Basic.lean @@ -1377,3 +1377,19 @@ instance [IsDomain R] (S : Submonoid R) [IsTorsionFree R M] : isTorsionFree (LocalizedModule.mkLinearMap S M) S end IsLocalizedModule + +/-! +## Localizations of modules away from an element +-/ + +/-- Given `x : R` and `f : M →ₗ[R] M'`, `IsLocalizedModule.Away x f` states that `M'` + is isomorphic to the localization of `M` at the submonoid generated by `x`. -/ +protected abbrev IsLocalizedModule.Away {R M M' : Type*} [CommSemiring R] (x : R) [AddCommMonoid M] + [Module R M] [AddCommMonoid M'] [Module R M'] (f : M →ₗ[R] M') := + IsLocalizedModule (Submonoid.powers x) f + +/-- Given `x : R`, `LocalizedModule.Away x M` is the localization of `M` at the + submonoid generated by `x`. -/ +protected abbrev LocalizedModule.Away {R : Type*} [CommSemiring R] (x : R) + (M : Type*) [AddCommMonoid M] [Module R M] := + LocalizedModule (Submonoid.powers x) M diff --git a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean index 28f7e535af..b5d687ac24 100644 --- a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean +++ b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean @@ -112,7 +112,7 @@ theorem toOpen_res (U V : Opens (PrimeSpectrum.Top R)) (i : V ⟶ U) : toOpen M U ≫ (modulesSpecToSheaf.obj (tilde M)).presheaf.map i.op = toOpen M V := rfl -instance (f : R) : IsLocalizedModule (.powers f) (toOpen M (basicOpen f)).hom := +instance (f : R) : IsLocalizedModule.Away f (toOpen M (basicOpen f)).hom := .of_linearEquiv (.powers f) (StructureSheaf.toOpenₗ R M (basicOpen f)) ((modulesSpecToSheafIso M).app _).toLinearEquiv.symm diff --git a/Mathlib/AlgebraicGeometry/StructureSheaf.lean b/Mathlib/AlgebraicGeometry/StructureSheaf.lean index c1655ed34a..f425433310 100644 --- a/Mathlib/AlgebraicGeometry/StructureSheaf.lean +++ b/Mathlib/AlgebraicGeometry/StructureSheaf.lean @@ -403,7 +403,7 @@ variable (R M) in /-- The canonical linear map interpreting `s ∈ M_f` as a section of the structure sheaf on the basic open defined by `f ∈ R`. -/ def toBasicOpenₗ (f : R) : - LocalizedModule (.powers f) M →ₗ[R] Γ(M, PrimeSpectrum.basicOpen f) := + LocalizedModule.Away f M →ₗ[R] Γ(M, PrimeSpectrum.basicOpen f) := IsLocalizedModule.lift (.powers f) (LocalizedModule.mkLinearMap ..) (toOpenₗ R M _) <| by simp only [Subtype.forall] exact Submonoid.powers_le (P := (IsUnit.submonoid _).comap (algebraMap R _)).mpr @@ -511,7 +511,7 @@ theorem toBasicOpenₗ_surjective (f : R) : Function.Surjective (toBasicOpenₗ simp_rw [one_smul, Finset.smul_sum, Submonoid.smul_def, smul_comm (b i), hab _ i, ← smul_assoc, ← Finset.sum_smul, hc] -public instance (f : R) : IsLocalizedModule (.powers f) (toOpenₗ R M (basicOpen f)) := by +public instance (f : R) : IsLocalizedModule.Away f (toOpenₗ R M (basicOpen f)) := by convert IsLocalizedModule.of_linearEquiv (.powers f) (LocalizedModule.mkLinearMap (.powers f) M) (.ofBijective _ ⟨toBasicOpenₗ_injective _, toBasicOpenₗ_surjective _⟩) ext x @@ -537,7 +537,7 @@ public lemma algebraMap_obj_top_bijective : set_option backward.isDefEq.respectTransparency false in public instance (f : R) : IsLocalization.Away f Γ(R, basicOpen f) := (isLocalizedModule_iff_isLocalization' _ _).mp <| - inferInstanceAs (IsLocalizedModule (.powers f) (toOpenₗ R R (basicOpen f))) + inferInstanceAs (IsLocalizedModule.Away f (toOpenₗ R R (basicOpen f))) end basicOpen diff --git a/Mathlib/RingTheory/LocalProperties/Exactness.lean b/Mathlib/RingTheory/LocalProperties/Exactness.lean index fa8cd9d6e9..50336a5d8a 100644 --- a/Mathlib/RingTheory/LocalProperties/Exactness.lean +++ b/Mathlib/RingTheory/LocalProperties/Exactness.lean @@ -10,7 +10,6 @@ public import Mathlib.RingTheory.LocalProperties.Submodule public import Mathlib.RingTheory.Localization.Algebra public import Mathlib.RingTheory.Localization.Away.Basic public import Mathlib.Algebra.Module.LocalizedModule.AtPrime -public import Mathlib.Algebra.Module.LocalizedModule.Away /-! # Local properties about linear maps diff --git a/Mathlib/RingTheory/LocalProperties/Submodule.lean b/Mathlib/RingTheory/LocalProperties/Submodule.lean index 38b71c21cb..014c000986 100644 --- a/Mathlib/RingTheory/LocalProperties/Submodule.lean +++ b/Mathlib/RingTheory/LocalProperties/Submodule.lean @@ -148,7 +148,7 @@ variable [∀ r : s, Module (Rₚ r) (Mₚ r)] [∀ r : s, IsScalarTower R (Rₚ r) (Mₚ r)] (f : ∀ r : s, M →ₗ[R] Mₚ r) - [∀ r : s, IsLocalizedModule (.powers r.1) (f r)] + [∀ r : s, IsLocalizedModule.Away r.1 (f r)] theorem Module.eq_of_isLocalized_span (x y : M) (h : ∀ r : s, f r x = f r y) : x = y := by suffices Module.eqIdeal R x y = ⊤ by simpa [Module.eqIdeal] using (eq_top_iff_one _).mp this diff --git a/Mathlib/RingTheory/Localization/Finiteness.lean b/Mathlib/RingTheory/Localization/Finiteness.lean index cb5a31c468..84ac4fc6d6 100644 --- a/Mathlib/RingTheory/Localization/Finiteness.lean +++ b/Mathlib/RingTheory/Localization/Finiteness.lean @@ -185,7 +185,7 @@ theorem of_localizationSpan_finite' (t : Finset R) (ht : Ideal.span (t : Set R) {Rₚ : ∀ (_ : t), Type*} [∀ (g : t), CommSemiring (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] [∀ (g : t), IsLocalization.Away g.val (Rₚ g)] [∀ (g : t), Module (Rₚ g) (Mₚ g)] [∀ (g : t), IsScalarTower R (Rₚ g) (Mₚ g)] - (f : ∀ (g : t), M →ₗ[R] Mₚ g) [∀ (g : t), IsLocalizedModule (Submonoid.powers g.val) (f g)] + (f : ∀ (g : t), M →ₗ[R] Mₚ g) [∀ (g : t), IsLocalizedModule.Away g.val (f g)] (H : ∀ (g : t), Module.Finite (Rₚ g) (Mₚ g)) : Module.Finite R M := by classical @@ -221,14 +221,14 @@ theorem of_localizationSpan' (t : Set R) (ht : Ideal.span t = ⊤) {Rₚ : ∀ (_ : t), Type*} [∀ (g : t), CommSemiring (Rₚ g)] [∀ (g : t), Algebra R (Rₚ g)] [h₁ : ∀ (g : t), IsLocalization.Away g.val (Rₚ g)] [∀ (g : t), Module (Rₚ g) (Mₚ g)] [∀ (g : t), IsScalarTower R (Rₚ g) (Mₚ g)] - (f : ∀ (g : t), M →ₗ[R] Mₚ g) [h₂ : ∀ (g : t), IsLocalizedModule (Submonoid.powers g.val) (f g)] + (f : ∀ (g : t), M →ₗ[R] Mₚ g) [h₂ : ∀ (g : t), IsLocalizedModule.Away g.val (f g)] (H : ∀ (g : t), Module.Finite (Rₚ g) (Mₚ g)) : Module.Finite R M := by rw [Ideal.span_eq_top_iff_finite] at ht obtain ⟨t', hc, ht'⟩ := ht have (g : t') : IsLocalization.Away g.val (Rₚ ⟨g.val, hc g.property⟩) := h₁ ⟨g.val, hc g.property⟩ - have (g : t') : IsLocalizedModule (Submonoid.powers g.val) + have (g : t') : IsLocalizedModule.Away g.val ((fun g ↦ f ⟨g.val, hc g.property⟩) g) := h₂ ⟨g.val, hc g.property⟩ apply of_localizationSpan_finite' t' ht' (fun g ↦ f ⟨g.val, hc g.property⟩) (fun g ↦ H ⟨g.val, hc g.property⟩) @@ -241,9 +241,9 @@ See `of_localizationSpan` for a version without the finite set assumption. -/ theorem of_localizationSpan_finite (t : Finset R) (ht : Ideal.span (t : Set R) = ⊤) (H : ∀ (g : t), Module.Finite (Localization.Away g.val) - (LocalizedModule (Submonoid.powers g.val) M)) : + (LocalizedModule.Away g.val M)) : Module.Finite R M := - let f (g : t) : M →ₗ[R] LocalizedModule (Submonoid.powers g.val) M := + let f (g : t) : M →ₗ[R] LocalizedModule.Away g.val M := LocalizedModule.mkLinearMap (Submonoid.powers g.val) M of_localizationSpan_finite' t ht f H @@ -251,9 +251,9 @@ theorem of_localizationSpan_finite (t : Finset R) (ht : Ideal.span (t : Set R) = then `M` is a finite `R`-module. -/ theorem of_localizationSpan (t : Set R) (ht : Ideal.span t = ⊤) (H : ∀ (g : t), Module.Finite (Localization.Away g.val) - (LocalizedModule (Submonoid.powers g.val) M)) : + (LocalizedModule.Away g.val M)) : Module.Finite R M := - let f (g : t) : M →ₗ[R] LocalizedModule (Submonoid.powers g.val) M := + let f (g : t) : M →ₗ[R] LocalizedModule.Away g.val M := LocalizedModule.mkLinearMap (Submonoid.powers g.val) M of_localizationSpan' t ht f H diff --git a/Mathlib/RingTheory/Localization/Free.lean b/Mathlib/RingTheory/Localization/Free.lean index 92f8594566..af9e0e7b7f 100644 --- a/Mathlib/RingTheory/Localization/Free.lean +++ b/Mathlib/RingTheory/Localization/Free.lean @@ -43,7 +43,7 @@ lemma Module.FinitePresentation.exists_basis_localizedModule_powers [IsLocalization S Rₛ] [Module.FinitePresentation R M] {I} [Finite I] (b : Basis I Rₛ M') : ∃ (r : R) (hr : r ∈ S) - (b' : Basis I (Localization (.powers r)) (LocalizedModule (.powers r) M)), + (b' : Basis I (Localization (.powers r)) (LocalizedModule.Away r M)), ∀ i, (LocalizedModule.lift (.powers r) f fun s ↦ IsLocalizedModule.map_units f ⟨s.1, SetLike.le_def.mp (Submonoid.powers_le.mpr hr) s.2⟩) (b' i) = b i := by have : Module.FinitePresentation R (I →₀ R) := Module.finitePresentation_of_projective _ _ @@ -79,8 +79,8 @@ lemma Module.FinitePresentation.exists_free_localizedModule_powers (Rₛ) [CommRing Rₛ] [Algebra R Rₛ] [Module Rₛ M'] [IsScalarTower R Rₛ M'] [Nontrivial Rₛ] [IsLocalization S Rₛ] [Module.FinitePresentation R M] [Module.Free Rₛ M'] : ∃ r, r ∈ S ∧ - Module.Free (Localization (.powers r)) (LocalizedModule (.powers r) M) ∧ - Module.finrank (Localization (.powers r)) (LocalizedModule (.powers r) M) = + Module.Free (Localization (.powers r)) (LocalizedModule.Away r M) ∧ + Module.finrank (Localization (.powers r)) (LocalizedModule.Away r M) = Module.finrank Rₛ M' := by let I := Module.Free.ChooseBasisIndex Rₛ M' let b : Basis I Rₛ M' := Module.Free.chooseBasis Rₛ M' diff --git a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean index 11c5b02be3..0e2381c60c 100644 --- a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean +++ b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean @@ -168,7 +168,7 @@ lemma freeLocus_eq_univ [Module.Finite R M] [Module.Flat R M] : lemma basicOpen_subset_freeLocus_iff [Module.FinitePresentation R M] {f : R} : (basicOpen f : Set (PrimeSpectrum R)) ⊆ freeLocus R M ↔ - Module.Projective (Localization.Away f) (LocalizedModule (.powers f) M) := by + Module.Projective (Localization.Away f) (LocalizedModule.Away f M) := by rw [← freeLocus_eq_univ_iff, freeLocus_localization, Set.preimage_eq_univ_iff, localization_away_comap_range _ f] diff --git a/Mathlib/RingTheory/Spectrum/Prime/Module.lean b/Mathlib/RingTheory/Spectrum/Prime/Module.lean index e5022d65de..97da996481 100644 --- a/Mathlib/RingTheory/Spectrum/Prime/Module.lean +++ b/Mathlib/RingTheory/Spectrum/Prime/Module.lean @@ -35,7 +35,7 @@ lemma IsLocalRing.closedPoint_mem_support [IsLocalRing R] [Nontrivial M] : /-- `M[1/f] = 0` if and only if `D(f) ∩ Supp M = 0`. -/ lemma LocalizedModule.subsingleton_iff_disjoint {f : R} : - Subsingleton (LocalizedModule (.powers f) M) ↔ + Subsingleton (LocalizedModule.Away f M) ↔ Disjoint ↑(PrimeSpectrum.basicOpen f) (Module.support R M) := by rw [subsingleton_iff_support_subset, PrimeSpectrum.basicOpen_eq_zeroLocus_compl, disjoint_compl_left_iff, Set.le_iff_subset] diff --git a/Mathlib/RingTheory/Support.lean b/Mathlib/RingTheory/Support.lean index 508da5dff7..f6dfd25967 100644 --- a/Mathlib/RingTheory/Support.lean +++ b/Mathlib/RingTheory/Support.lean @@ -97,7 +97,7 @@ lemma Module.annihilator_le_of_mem_support (hp : p ∈ Module.support R M) : exact le_trans ((Submodule.subtype _).annihilator_le_of_injective Subtype.val_injective) hm lemma LocalizedModule.subsingleton_iff_support_subset {f : R} : - Subsingleton (LocalizedModule (.powers f) M) ↔ + Subsingleton (LocalizedModule.Away f M) ↔ Module.support R M ⊆ PrimeSpectrum.zeroLocus {f} := by rw [LocalizedModule.subsingleton_iff] constructor @@ -221,7 +221,7 @@ lemma Module.support_eq_zeroLocus : then `M[1/f] = 0` for some `p ∈ D(f)`. -/ lemma LocalizedModule.exists_subsingleton_away (p : Ideal R) [p.IsPrime] [Subsingleton (LocalizedModule p.primeCompl M)] : - ∃ f ∉ p, Subsingleton (LocalizedModule (.powers f) M) := by + ∃ f ∉ p, Subsingleton (LocalizedModule.Away f M) := by have : ⟨p, inferInstance⟩ ∈ (Module.support R M)ᶜ := by simpa [Module.notMem_support_iff] rw [Module.support_eq_zeroLocus, ← Set.biUnion_of_singleton (Module.annihilator R M : Set R), @@ -233,7 +233,7 @@ lemma LocalizedModule.exists_subsingleton_away (p : Ideal R) [p.IsPrime] lemma IsLocalizedModule.exists_subsingleton_away {M' : Type*} [AddCommMonoid M'] [Module R M'] (l : M →ₗ[R] M') (p : Ideal R) [p.IsPrime] [IsLocalizedModule p.primeCompl l] [Subsingleton M'] : - ∃ f ∉ p, Subsingleton (LocalizedModule (.powers f) M) := by + ∃ f ∉ p, Subsingleton (LocalizedModule.Away f M) := by let e := IsLocalizedModule.iso p.primeCompl l have : Subsingleton (LocalizedModule p.primeCompl M) := e.subsingleton exact LocalizedModule.exists_subsingleton_away p From 47e40c50c9a9cf17ea45fb552bc0b4106e685f5c Mon Sep 17 00:00:00 2001 From: Jeremy Tan Jie Rui <54175463+Parcly-Taxel@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:36 +0000 Subject: [PATCH 24/65] feat: arbitrary-order induction on `Nat` (#38442) This can be used as e.g. ```lean induction n using stepInduction 3 with | base n hn => ... | step n ih => ... ``` The test file's examples are from a term project I did for an NUS module taught by Olivier Danvy himself. Co-authored-by: Parcly Taxel --- Mathlib/Data/Nat/Init.lean | 17 +++++++++++-- MathlibTest/StepInduction.lean | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 MathlibTest/StepInduction.lean diff --git a/Mathlib/Data/Nat/Init.lean b/Mathlib/Data/Nat/Init.lean index 5cd32bff81..83c1987137 100644 --- a/Mathlib/Data/Nat/Init.lean +++ b/Mathlib/Data/Nat/Init.lean @@ -271,12 +271,25 @@ lemma le_induction {m : ℕ} {P : ∀ n, m ≤ n → Prop} (base : P m m.le_refl @Nat.leRec (motive := P) _ base succ /-- Induction principle deriving the next case from the two previous ones. -/ -def twoStepInduction {P : ℕ → Sort*} (zero : P 0) (one : P 1) - (more : ∀ n, P n → P (n + 1) → P (n + 2)) : ∀ a, P a +@[elab_as_elim] +def twoStepInduction {motive : ℕ → Sort*} (zero : motive 0) (one : motive 1) + (more : ∀ n, motive n → motive (n + 1) → motive (n + 2)) : ∀ a, motive a | 0 => zero | 1 => one | _ + 2 => more _ (twoStepInduction zero one more _) (twoStepInduction zero one more _) +/-- Induction principle deriving the next case from the `k` previous ones. Use as +``` +induction n using stepInduction 3 with +| base n hn => ... +| step n ih => ... +``` -/ +@[elab_as_elim] +def stepInduction {motive : ℕ → Sort*} (k : ℕ) (base : ∀ i < k, motive i) + (step : ∀ n, (∀ i < k, motive (n + i)) → motive (n + k)) (a : ℕ) : motive a := + if h : a < k then base _ h else + (show a - k + k = a by lia) ▸ step (a - k) fun _ _ ↦ stepInduction k base step _ + @[elab_as_elim] protected theorem strong_induction_on {p : ℕ → Prop} (n : ℕ) (h : ∀ n, (∀ m < n, p m) → p n) : p n := diff --git a/MathlibTest/StepInduction.lean b/MathlibTest/StepInduction.lean new file mode 100644 index 0000000000..3c3b7d7cfd --- /dev/null +++ b/MathlibTest/StepInduction.lean @@ -0,0 +1,46 @@ +module + +import Mathlib.Data.Nat.Fib.Basic + +/-! +The examples below are ported from the [Rocq supplementary material](https://zenodo.org/records/13855491) +accompanying Olivier Danvy's "[Nested Summations](https://doi.org/10.1145/3694848.3694858)". +-/ + +open Nat Finset + +def fibf (f : ℕ → ℕ) : ℕ → ℕ + | 0 => f 0 + | 1 => f 1 + | n + 2 => fibf f (n + 1) + fibf f n + +example {f : ℕ → ℕ} {n : ℕ} : fibf f (n + 1) = f 1 * fib (n + 1) + f 0 * fib n := by + induction n using stepInduction 2 with + | base n hn => + obtain rfl | rfl : n = 0 ∨ n = 1 := by lia + all_goals simp [fibf] + | step n ih => grind [fibf, fib_add_two, ih 0 (by decide)] + +def a6356 : ℕ → ℕ + | 0 => 1 + | 1 => 3 + | 2 => 6 + | n + 3 => 2 * a6356 (n + 2) + a6356 (n + 1) - a6356 n + +def a6356Sum (f : ℕ → ℕ) : ℕ → ℕ → ℕ + | 0, i => f (2 - i) + | n + 1, i => ∑ j ∈ range (3 - i), a6356Sum f n j + +lemma strictMono_a6356 : StrictMono a6356 := by + refine strictMono_nat_of_lt_succ fun n ↦ ?_ + induction n using stepInduction 3 with + | base n hn => decide +revert + | step n ih => grind [a6356] + +example {n : ℕ} : a6356Sum (· - 1) (n + 1) 0 = a6356 n := by + induction n using stepInduction 3 with + | base n hn => decide +revert + | step n ih => + have := strictMono_a6356 (lt_add_one n) + rw [← add_left_inj (a6356 n), a6356, Nat.sub_add_cancel (by lia)] + grind [sum_range_succ, sum_range_one, a6356Sum, ih 0 (by decide)] From 76ee00a560e037854d0b1779c0523fa1ec650927 Mon Sep 17 00:00:00 2001 From: Noah Walker <30136151+NoahW314@users.noreply.github.com> Date: Sun, 10 May 2026 01:42:39 +0000 Subject: [PATCH 25/65] feat(Data/Finsupp/Weight): add `Finsupp.degree_mapDomain` (#38656) Generalize a result by removing an unnecessary hypothesis. Also simplifies the proof. Co-authored-by: NoahW314 --- Mathlib/Data/Finsupp/Weight.lean | 15 ++++++++------- Mathlib/Order/Filter/TendstoCofinite.lean | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Mathlib/Data/Finsupp/Weight.lean b/Mathlib/Data/Finsupp/Weight.lean index cf9b669dd6..d6cc5fd6d7 100644 --- a/Mathlib/Data/Finsupp/Weight.lean +++ b/Mathlib/Data/Finsupp/Weight.lean @@ -265,13 +265,14 @@ lemma range_single_one : obtain ⟨a, rfl⟩ := (Finsupp.sum_eq_one_iff _).mp hp use a -theorem degree_mapDomain_eq_of_subsingletonAddUnits {τ : Type*} (f : σ → τ) [AddCommMonoid M] - [Subsingleton (AddUnits M)] (x : σ →₀ M) : degree (x.mapDomain f) = degree x := by - classical - trans (x.mapDomain f).sum (fun _ ↦ id) - · simp [degree, sum] - · simpa [sum, mapDomain_support_of_subsingletonAddUnits, degree] using Finset.sum_image' _ - (fun _ _ ↦ mapDomain_apply_eq_sum ..) +@[simp] +theorem degree_mapDomain {τ : Type*} (f : σ → τ) [AddCommMonoid M] (x : σ →₀ M) : + degree (x.mapDomain f) = degree x := by + simp [mapDomain, sum] + dsimp [degree_apply] + +@[deprecated (since := "2026-04-27")] +alias degree_mapDomain_eq_of_subsingletonAddUnits := degree_mapDomain theorem degree_comapDomain_le_of_canonicallyOrderedAdd {τ : Type*} {f : σ → τ} [AddCommMonoid M] [PartialOrder M] [CanonicallyOrderedAdd M] {x : τ →₀ M} (hf : Set.InjOn f (f ⁻¹' x.support)) : diff --git a/Mathlib/Order/Filter/TendstoCofinite.lean b/Mathlib/Order/Filter/TendstoCofinite.lean index 9027389dfc..b056263b12 100644 --- a/Mathlib/Order/Filter/TendstoCofinite.lean +++ b/Mathlib/Order/Filter/TendstoCofinite.lean @@ -117,7 +117,7 @@ theorem Finsupp.mapDomain_tendstoCofinite [TendstoCofinite f] : simp only [Set.subset_def, Set.mem_preimage, Set.mem_singleton_iff, Set.mem_image, Set.mem_setOf_eq] refine fun y hy ↦ ⟨y.comapDomain e e.injective.injOn, ?_, embDomain_comapDomain ?_⟩ - · rw [← hy, degree_mapDomain_eq_of_subsingletonAddUnits] + · rw [← hy, degree_mapDomain] exact degree_comapDomain_le_of_canonicallyOrderedAdd .. · suffices y.support ⊆ s by simpa [e] simpa [← hy, mapDomain, sum, Finset.subset_iff, single_apply, s] using From 15ac533632bf5526196c27cb34bc783fa669ea1f Mon Sep 17 00:00:00 2001 From: Wrenna Robson Date: Sun, 10 May 2026 01:42:41 +0000 Subject: [PATCH 26/65] fix: rename `Pi.prod` to `Function.prod` (#38963) Renames `Pi.prod` to `Function.prod` in order to allow for dot notation to work in the non-dependent case. Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- Archive/Sensitivity.lean | 2 +- Mathlib/Algebra/Algebra/NonUnitalHom.lean | 14 +++++----- Mathlib/Algebra/Algebra/Prod.lean | 4 +-- Mathlib/Algebra/BigOperators/Finprod.lean | 2 +- Mathlib/Algebra/Exact.lean | 2 +- Mathlib/Algebra/Group/Prod.lean | 8 +++--- Mathlib/Algebra/Lie/Prod.lean | 2 +- Mathlib/Algebra/MvPolynomial/Equiv.lean | 2 +- Mathlib/Algebra/MvPolynomial/Eval.lean | 2 +- Mathlib/Algebra/Notation/Pi/Defs.lean | 15 ++++++----- Mathlib/Algebra/Star/StarAlgHom.lean | 14 +++++----- Mathlib/Combinatorics/Nullstellensatz.lean | 4 +-- .../LinearAlgebra/AffineSpace/AffineMap.lean | 4 +-- .../LinearAlgebra/Dimension/DivisionRing.lean | 3 ++- Mathlib/LinearAlgebra/Goursat.lean | 26 ++++++++----------- Mathlib/LinearAlgebra/Prod.lean | 10 +++---- Mathlib/LinearAlgebra/QuadraticForm/Dual.lean | 2 +- Mathlib/Logic/Function/Defs.lean | 13 ++++++++++ Mathlib/MeasureTheory/Integral/Prod.lean | 2 +- Mathlib/MeasureTheory/Measure/Prod.lean | 11 ++++---- Mathlib/NumberTheory/Height/Basic.lean | 2 +- Mathlib/SetTheory/Cardinal/Finite.lean | 2 +- .../Topology/Algebra/ContinuousAffineMap.lean | 2 +- .../Topology/Algebra/InfiniteSum/Basic.lean | 6 ++--- .../Algebra/InfiniteSum/Constructions.lean | 2 +- Mathlib/Topology/Inseparable.lean | 2 +- .../LocallyUniformConvergence.lean | 4 +-- 27 files changed, 88 insertions(+), 74 deletions(-) diff --git a/Archive/Sensitivity.lean b/Archive/Sensitivity.lean index 53a2ef0b6f..b48847812a 100644 --- a/Archive/Sensitivity.lean +++ b/Archive/Sensitivity.lean @@ -329,7 +329,7 @@ set_option backward.isDefEq.respectTransparency false in theorem g_injective : Injective (g m) := by rw [g] intro x₁ x₂ h - simp only [V, LinearMap.prod_apply, LinearMap.id_apply, Prod.mk_inj, Pi.prod] at h + simp only [V, LinearMap.prod_apply, LinearMap.id_apply, Prod.mk_inj, Function.prod_apply] at h exact h.right set_option backward.isDefEq.respectTransparency false in diff --git a/Mathlib/Algebra/Algebra/NonUnitalHom.lean b/Mathlib/Algebra/Algebra/NonUnitalHom.lean index d3986be73b..f84999ab77 100644 --- a/Mathlib/Algebra/Algebra/NonUnitalHom.lean +++ b/Mathlib/Algebra/Algebra/NonUnitalHom.lean @@ -371,13 +371,13 @@ variable [DistribMulAction R C] /-- The prod of two morphisms is a morphism. -/ @[simps] def prod (f : A →ₙₐ[R] B) (g : A →ₙₐ[R] C) : A →ₙₐ[R] B × C where - toFun := Pi.prod f g - map_zero' := by simp only [Pi.prod, Prod.mk_zero_zero, map_zero] - map_add' x y := by simp only [Pi.prod, Prod.mk_add_mk, map_add] - map_mul' x y := by simp only [Pi.prod, Prod.mk_mul_mk, map_mul] - map_smul' c x := by simp only [Pi.prod, map_smul, MonoidHom.id_apply, Prod.smul_mk] + toFun := Function.prod f g + map_zero' := by simp only [Function.prod_apply, Prod.mk_zero_zero, map_zero] + map_add' x y := by simp only [Function.prod_apply, Prod.mk_add_mk, map_add] + map_mul' x y := by simp only [Function.prod_apply, Prod.mk_mul_mk, map_mul] + map_smul' c x := by simp only [Function.prod_apply, map_smul, MonoidHom.id_apply, Prod.smul_mk] -theorem coe_prod (f : A →ₙₐ[R] B) (g : A →ₙₐ[R] C) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : A →ₙₐ[R] B) (g : A →ₙₐ[R] C) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] @@ -390,7 +390,7 @@ theorem snd_prod (f : A →ₙₐ[R] B) (g : A →ₙₐ[R] C) : (snd R B C).com @[simp] theorem prod_fst_snd : prod (fst R A B) (snd R A B) = 1 := - coe_injective Pi.prod_fst_snd + coe_injective Function.prod_fst_snd /-- Taking the product of two maps with the same domain is equivalent to taking the product of their codomains. -/ diff --git a/Mathlib/Algebra/Algebra/Prod.lean b/Mathlib/Algebra/Algebra/Prod.lean index 46058dff5a..66cd3bf80c 100644 --- a/Mathlib/Algebra/Algebra/Prod.lean +++ b/Mathlib/Algebra/Algebra/Prod.lean @@ -77,7 +77,7 @@ theorem snd_apply (a) : snd R A B a = a.2 := rfl variable {R} -/-- The `Pi.prod` of two morphisms is a morphism. -/ +/-- The `Function.prod` of two morphisms is a morphism. -/ @[simps!] def prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : A →ₐ[R] B × C := { f.toRingHom.prod g.toRingHom with @@ -85,7 +85,7 @@ def prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : A →ₐ[R] B × C := simp only [toRingHom_eq_coe, RingHom.toFun_eq_coe, RingHom.prod_apply, coe_toRingHom, commutes, Prod.algebraMap_apply] } -theorem coe_prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : A →ₐ[R] B) (g : A →ₐ[R] C) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] diff --git a/Mathlib/Algebra/BigOperators/Finprod.lean b/Mathlib/Algebra/BigOperators/Finprod.lean index 0df8c87f58..1de4d50576 100644 --- a/Mathlib/Algebra/BigOperators/Finprod.lean +++ b/Mathlib/Algebra/BigOperators/Finprod.lean @@ -277,7 +277,7 @@ lemma one_le_finprod {M : Type*} [CommMonoidWithZero M] [Preorder M] [ZeroLEOneC theorem MonoidHom.map_finprod_plift (f : M →* N) (g : α → M) (h : HasFiniteMulSupport <| g ∘ PLift.down) : f (∏ᶠ x, g x) = ∏ᶠ x, f (g x) := by rw [finprod_eq_prod_plift_of_mulSupport_subset h.coe_toFinset.ge, - finprod_eq_prod_plift_of_mulSupport_subset, map_prod] + finprod_eq_prod_plift_of_mulSupport_subset, _root_.map_prod] rw [h.coe_toFinset] exact mulSupport_comp_subset f.map_one (g ∘ PLift.down) diff --git a/Mathlib/Algebra/Exact.lean b/Mathlib/Algebra/Exact.lean index 0457b644d8..5c0e741a20 100644 --- a/Mathlib/Algebra/Exact.lean +++ b/Mathlib/Algebra/Exact.lean @@ -428,7 +428,7 @@ def Exact.splitInjectiveEquiv have h₂ : ∀ x, g (f x) = 0 := congr_fun h.comp_eq_zero constructor · intro x y e - simp only [prod_apply, Pi.prod, Prod.mk.injEq] at e + simp only [LinearMap.prod_apply, Function.prod_apply, Prod.mk.injEq] at e obtain ⟨z, hz⟩ := (h (x - y)).mp (by simpa [sub_eq_zero] using e.2) rw [← sub_eq_zero, ← hz, ← h₁ z, hz, map_sub, e.1, sub_self, map_zero] · rintro ⟨x, y⟩ diff --git a/Mathlib/Algebra/Group/Prod.lean b/Mathlib/Algebra/Group/Prod.lean index 7b6de8812c..2b516ede3c 100644 --- a/Mathlib/Algebra/Group/Prod.lean +++ b/Mathlib/Algebra/Group/Prod.lean @@ -221,11 +221,11 @@ theorem coe_snd : ⇑(snd M N) = Prod.snd := `f.prod g : AddHom M (N × P)` given by `(f.prod g) x = (f x, g x)` -/] protected def prod (f : M →ₙ* N) (g : M →ₙ* P) : M →ₙ* N × P where - toFun := Pi.prod f g + toFun := Function.prod f g map_mul' x y := Prod.ext (f.map_mul x y) (g.map_mul x y) @[to_additive coe_prod] -theorem coe_prod (f : M →ₙ* N) (g : M →ₙ* P) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : M →ₙ* N) (g : M →ₙ* P) : ⇑(f.prod g) = Function.prod f g := rfl @[to_additive (attr := simp) prod_apply] @@ -387,12 +387,12 @@ given by `(f.prod g) x = (f x, g x)`. -/ `f.prod g : M →+ N × P` given by `(f.prod g) x = (f x, g x)` -/] protected def prod (f : M →* N) (g : M →* P) : M →* N × P where - toFun := Pi.prod f g + toFun := Function.prod f g map_one' := Prod.ext f.map_one g.map_one map_mul' x y := Prod.ext (f.map_mul x y) (g.map_mul x y) @[to_additive coe_prod] -theorem coe_prod (f : M →* N) (g : M →* P) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : M →* N) (g : M →* P) : ⇑(f.prod g) = Function.prod f g := rfl @[to_additive (attr := simp) prod_apply] diff --git a/Mathlib/Algebra/Lie/Prod.lean b/Mathlib/Algebra/Lie/Prod.lean index 21936d6d5d..48e9844cd6 100644 --- a/Mathlib/Algebra/Lie/Prod.lean +++ b/Mathlib/Algebra/Lie/Prod.lean @@ -99,7 +99,7 @@ def prod (f : L →ₗ⁅R⁆ L₁) (g : L →ₗ⁅R⁆ L₂) : L →ₗ⁅R⁆ toLinearMap := LinearMap.prod f g map_lie' := by simp -theorem coe_prod (f : L →ₗ⁅R⁆ L₁) (g : L →ₗ⁅R⁆ L₂) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : L →ₗ⁅R⁆ L₁) (g : L →ₗ⁅R⁆ L₂) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] diff --git a/Mathlib/Algebra/MvPolynomial/Equiv.lean b/Mathlib/Algebra/MvPolynomial/Equiv.lean index 674bcf626b..a66ed77243 100644 --- a/Mathlib/Algebra/MvPolynomial/Equiv.lean +++ b/Mathlib/Algebra/MvPolynomial/Equiv.lean @@ -639,7 +639,7 @@ theorem finSuccEquiv_coeff_coeff (m : Fin n →₀ ℕ) (f : MvPolynomial (Fin ( | monomial j r => simp only [finSuccEquiv_apply, coe_eval₂Hom, eval₂_monomial, RingHom.coe_comp, Finsupp.prod_pow, Polynomial.coeff_C_mul, coeff_C_mul, coeff_monomial, Fin.prod_univ_succ, Fin.cases_zero, - Fin.cases_succ, ← map_prod, ← map_pow, Function.comp_apply] + Fin.cases_succ, ← _root_.map_prod, ← map_pow, Function.comp_apply] rw [← mul_boole, mul_comm (Polynomial.X ^ j 0), Polynomial.coeff_C_mul_X_pow]; congr 1 obtain rfl | hjmi := eq_or_ne j (m.cons i) · simpa only [cons_zero, cons_succ, if_pos rfl, monomial_eq, C_1, one_mul, diff --git a/Mathlib/Algebra/MvPolynomial/Eval.lean b/Mathlib/Algebra/MvPolynomial/Eval.lean index 574e7e2fc7..0e13948de3 100644 --- a/Mathlib/Algebra/MvPolynomial/Eval.lean +++ b/Mathlib/Algebra/MvPolynomial/Eval.lean @@ -197,7 +197,7 @@ theorem map_eval₂Hom [CommSemiring S₂] (f : R →+* S₁) (g : σ → S₁) theorem eval₂Hom_monomial (f : R →+* S₁) (g : σ → S₁) (d : σ →₀ ℕ) (r : R) : eval₂Hom f g (monomial d r) = f r * d.prod fun i k => g i ^ k := by - simp only [monomial_eq, map_mul, eval₂Hom_C, Finsupp.prod, map_prod, map_pow, eval₂Hom_X'] + simp only [coe_eval₂Hom, eval₂_monomial] @[simp] theorem eval₂Hom_smul (f : R →+* S₁) (g : σ → S₁) (r : R) (P : MvPolynomial σ R) : diff --git a/Mathlib/Algebra/Notation/Pi/Defs.lean b/Mathlib/Algebra/Notation/Pi/Defs.lean index 56fb6e8772..77be7fd2c8 100644 --- a/Mathlib/Algebra/Notation/Pi/Defs.lean +++ b/Mathlib/Algebra/Notation/Pi/Defs.lean @@ -7,6 +7,8 @@ module public import Mathlib.Algebra.Notation.Defs public import Mathlib.Tactic.Push.Attr +public import Mathlib.Logic.Function.Defs +public import Batteries.Tactic.Alias /-! # Notation for algebraic operators on pi types @@ -26,13 +28,14 @@ variable {ι α β : Type*} {G M R : ι → Type*} namespace Pi --- TODO: Do we really need this definition? If so, where to put it? -/-- The mapping into a product type built from maps into each component. -/ -@[simp] -protected def prod {α β : ι → Type*} (f : ∀ i, α i) (g : ∀ i, β i) (i : ι) : α i × β i := (f i, g i) +@[deprecated (since := "2026-04-21")] +alias prod := Function.prod + +@[deprecated (since := "2026-04-21")] +alias prod_fst_snd := Function.prod_fst_snd -lemma prod_fst_snd : Pi.prod (Prod.fst : α × β → α) (Prod.snd : α × β → β) = id := rfl -lemma prod_snd_fst : Pi.prod (Prod.snd : α × β → β) (Prod.fst : α × β → α) = .swap := rfl +@[deprecated (since := "2026-04-21")] +alias prod_snd_fst := Function.prod_snd_fst /-! `1`, `0`, `+`, `*`, `+ᵥ`, `•`, `^`, `-`, `⁻¹`, and `/` are defined pointwise. -/ diff --git a/Mathlib/Algebra/Star/StarAlgHom.lean b/Mathlib/Algebra/Star/StarAlgHom.lean index c72de1d2ca..75751b30f7 100644 --- a/Mathlib/Algebra/Star/StarAlgHom.lean +++ b/Mathlib/Algebra/Star/StarAlgHom.lean @@ -488,13 +488,13 @@ def snd : A × B →⋆ₙₐ[R] B := variable {R A B C} -/-- The `Pi.prod` of two morphisms is a morphism. -/ +/-- The `Function.prod` of two morphisms is a morphism. -/ @[simps!] def prod (f : A →⋆ₙₐ[R] B) (g : A →⋆ₙₐ[R] C) : A →⋆ₙₐ[R] B × C := { f.toNonUnitalAlgHom.prod g.toNonUnitalAlgHom with - map_star' := fun x => by simp [map_star, Prod.star_def] } + map_star' := fun x => by simp [map_star, Prod.ext_iff] } -theorem coe_prod (f : A →⋆ₙₐ[R] B) (g : A →⋆ₙₐ[R] C) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : A →⋆ₙₐ[R] B) (g : A →⋆ₙₐ[R] C) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] @@ -507,7 +507,7 @@ theorem snd_prod (f : A →⋆ₙₐ[R] B) (g : A →⋆ₙₐ[R] C) : (snd R B @[simp] theorem prod_fst_snd : prod (fst R A B) (snd R A B) = 1 := - DFunLike.coe_injective Pi.prod_fst_snd + DFunLike.coe_injective Function.prod_fst_snd /-- Taking the product of two maps with the same domain is equivalent to taking the product of their codomains. -/ @@ -593,12 +593,12 @@ def snd : A × B →⋆ₐ[R] B := variable {R A B C} -/-- The `Pi.prod` of two morphisms is a morphism. -/ +/-- The `Function.prod` of two morphisms is a morphism. -/ @[simps!] def prod (f : A →⋆ₐ[R] B) (g : A →⋆ₐ[R] C) : A →⋆ₐ[R] B × C := { f.toAlgHom.prod g.toAlgHom with map_star' := fun x => by simp [Prod.star_def, map_star] } -theorem coe_prod (f : A →⋆ₐ[R] B) (g : A →⋆ₐ[R] C) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : A →⋆ₐ[R] B) (g : A →⋆ₐ[R] C) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] @@ -611,7 +611,7 @@ theorem snd_prod (f : A →⋆ₐ[R] B) (g : A →⋆ₐ[R] C) : (snd R B C).com @[simp] theorem prod_fst_snd : prod (fst R A B) (snd R A B) = 1 := - DFunLike.coe_injective Pi.prod_fst_snd + DFunLike.coe_injective Function.prod_fst_snd /-- Taking the product of two maps with the same domain is equivalent to taking the product of their codomains. -/ diff --git a/Mathlib/Combinatorics/Nullstellensatz.lean b/Mathlib/Combinatorics/Nullstellensatz.lean index 59fee42216..12ebec3ddb 100644 --- a/Mathlib/Combinatorics/Nullstellensatz.lean +++ b/Mathlib/Combinatorics/Nullstellensatz.lean @@ -174,7 +174,7 @@ private lemma Alon.of_mem_P_support {ι : Type*} (i : ι) (S : Finset R) (m : ι (hm : m ∈ (Alon.P S i).support) : ∃ e ≤ S.card, m = single i e := by classical - have hP : Alon.P S i = .rename (fun _ ↦ i) (Alon.P S ()) := by simp [Alon.P, map_prod] + have hP : Alon.P S i = .rename (fun _ ↦ i) (Alon.P S ()) := by simp [Alon.P] rw [hP, support_rename_of_injective (Function.injective_of_subsingleton _)] at hm simp only [Finset.mem_image, mem_support_iff, ne_eq] at hm obtain ⟨e, he, hm⟩ := hm @@ -228,7 +228,7 @@ theorem combinatorial_nullstellensatz_exists_linearCombination intro i _ rw [smul_eq_mul, map_mul] convert mul_zero _ - rw [Alon.P, map_prod] + rw [Alon.P, _root_.map_prod] apply Finset.prod_eq_zero (hx i) simp diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineMap.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineMap.lean index bb6718dfd5..63be48109f 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/AffineMap.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/AffineMap.lean @@ -451,11 +451,11 @@ theorem image_vsub_image {s t : Set P1} (f : P1 →ᵃ[k] P2) : /-- The product of two affine maps is an affine map. -/ @[simps linear] def prod (f : P1 →ᵃ[k] P2) (g : P1 →ᵃ[k] P3) : P1 →ᵃ[k] P2 × P3 where - toFun := Pi.prod f g + toFun := Function.prod f g linear := f.linear.prod g.linear map_vadd' := by simp -theorem coe_prod (f : P1 →ᵃ[k] P2) (g : P1 →ᵃ[k] P3) : prod f g = Pi.prod f g := +theorem coe_prod (f : P1 →ᵃ[k] P2) (g : P1 →ᵃ[k] P3) : prod f g = Function.prod f g := rfl @[simp] diff --git a/Mathlib/LinearAlgebra/Dimension/DivisionRing.lean b/Mathlib/LinearAlgebra/Dimension/DivisionRing.lean index b37b76bb2f..b01b9a2073 100644 --- a/Mathlib/LinearAlgebra/Dimension/DivisionRing.lean +++ b/Mathlib/LinearAlgebra/Dimension/DivisionRing.lean @@ -97,7 +97,8 @@ theorem rank_add_rank_split (db : V₂ →ₗ[K] V) (eb : V₃ →ₗ[K] V) (cd rintro ⟨d, e⟩ have h := eq₂ d (-e) simp only [add_eq_zero_iff_eq_neg, LinearMap.prod_apply, mem_ker, - Prod.mk_inj, coprod_apply, map_neg, neg_apply, LinearMap.mem_range, Pi.prod] at h ⊢ + Prod.mk_inj, coprod_apply, map_neg, neg_apply, LinearMap.mem_range, + Function.prod_apply] at h ⊢ grind end diff --git a/Mathlib/LinearAlgebra/Goursat.lean b/Mathlib/LinearAlgebra/Goursat.lean index 4090de1ada..b39c302ba9 100644 --- a/Mathlib/LinearAlgebra/Goursat.lean +++ b/Mathlib/LinearAlgebra/Goursat.lean @@ -119,24 +119,20 @@ lemma goursat : ∃ (M' : Submodule R M) (N' : Submodule R N) (M'' : Submodule R obtain ⟨e, he⟩ := goursat_surjective hL₁' hL₂' use M', N', L'.goursatFst, L'.goursatSnd, e rw [← he] - simp only [LinearMap.range_comp, Submodule.range_subtype, L'] + simp only [LinearMap.range_comp, Submodule.range_subtype, L', M', N', P, Q] rw [comap_map_eq_self] · ext ⟨m, n⟩ constructor - · intro hmn - simp only [mem_map, LinearMap.mem_range, prod_apply, Subtype.exists, Prod.exists, coe_prodMap, - coe_subtype, Prod.map_apply, Prod.mk.injEq, exists_and_right, exists_eq_right_right, - exists_eq_right, M', N', fst_apply, snd_apply] - exact ⟨⟨n, hmn⟩, ⟨m, hmn⟩, ⟨m, n, hmn, rfl⟩⟩ - · simp only [mem_map, LinearMap.mem_range, prod_apply, Subtype.exists, Prod.exists, - coe_prodMap, coe_subtype, Prod.map_apply, Prod.mk.injEq, exists_and_right, - exists_eq_right_right, exists_eq_right, forall_exists_index, Pi.prod] - rintro hm hn m₁ n₁ hm₁n₁ ⟨hP, hQ⟩ - simp only [Subtype.ext_iff] at hP hQ - rwa [← hP, ← hQ] + · simp only [mem_map, LinearMap.mem_range, LinearMap.prod_apply, Function.prod_apply, + Subtype.exists, Prod.exists, LinearMap.prodMap_apply, subtype_apply, Prod.mk.injEq, + Subtype.ext_iff, submoduleMap_coe_apply, fst_apply, snd_apply] + grind + · simp only [mem_map, LinearMap.mem_range, LinearMap.prod_apply, Function.prod_apply, + Subtype.exists, Prod.exists, LinearMap.prodMap_apply, subtype_apply, Prod.mk.injEq, + snd_apply, fst_apply, Subtype.ext_iff, submoduleMap_coe_apply] + grind · convert goursatFst_prod_goursatSnd_le (range <| P.prod Q) - ext ⟨m, n⟩ - simp_rw [mem_ker, coe_prodMap, Prod.map_apply, Submodule.mem_prod, Prod.zero_eq_mk, - Prod.ext_iff, ← mem_ker, ker_mkQ] + simp only [ker_prodMap, ker_mkQ, Submodule.ext_iff] + grind end Submodule diff --git a/Mathlib/LinearAlgebra/Prod.lean b/Mathlib/LinearAlgebra/Prod.lean index 6416808409..21f532a1db 100644 --- a/Mathlib/LinearAlgebra/Prod.lean +++ b/Mathlib/LinearAlgebra/Prod.lean @@ -93,11 +93,11 @@ theorem snd_surjective : Function.Surjective (snd R M M₂) := fun x => ⟨(0, x /-- The prod of two linear maps is a linear map. -/ @[simps] def prod (f : M →ₗ[R] M₂) (g : M →ₗ[R] M₃) : M →ₗ[R] M₂ × M₃ where - toFun := Pi.prod f g - map_add' x y := by simp only [Pi.prod, Prod.mk_add_mk, map_add] - map_smul' c x := by simp only [Pi.prod, Prod.smul_mk, map_smul, RingHom.id_apply] + toFun := Function.prod f g + map_add' x y := by simp only [Function.prod_apply, Prod.mk_add_mk, map_add] + map_smul' c x := by simp only [Function.prod_apply, Prod.smul_mk, map_smul, RingHom.id_apply] -theorem coe_prod (f : M →ₗ[R] M₂) (g : M →ₗ[R] M₃) : ⇑(f.prod g) = Pi.prod f g := +theorem coe_prod (f : M →ₗ[R] M₂) (g : M →ₗ[R] M₃) : ⇑(f.prod g) = Function.prod f g := rfl @[simp] @@ -850,7 +850,7 @@ theorem range_prod_eq {f : M →ₗ[R] M₂} {g : M →ₗ[R] M₃} (h : ker f range (prod f g) = (range f).prod (range g) := by refine le_antisymm (f.range_prod_le g) ?_ simp only [SetLike.le_def, prod_apply, mem_range, mem_prod, exists_imp, and_imp, - Prod.forall, Pi.prod] + Prod.forall, Function.prod_apply] rintro _ _ x rfl y rfl -- Note: https://github.com/leanprover-community/mathlib4/pull/8386 had to specify `(f := f)` simp only [Prod.mk_inj, ← sub_mem_ker_iff (f := f)] diff --git a/Mathlib/LinearAlgebra/QuadraticForm/Dual.lean b/Mathlib/LinearAlgebra/QuadraticForm/Dual.lean index e607498ea3..f4f8519a9a 100644 --- a/Mathlib/LinearAlgebra/QuadraticForm/Dual.lean +++ b/Mathlib/LinearAlgebra/QuadraticForm/Dual.lean @@ -140,7 +140,7 @@ def toDualProd (Q : QuadraticForm R M) [Invertible (2 : R)] : map_app' x := by dsimp only [associated, associatedHom] dsimp only [LinearMap.smul_apply, LinearMap.coe_mk, AddHom.coe_mk, AddHom.toFun_eq_coe, - LinearMap.coe_toAddHom, LinearMap.prod_apply, Pi.prod, LinearMap.add_apply, + LinearMap.coe_toAddHom, LinearMap.prod_apply, Function.prod_apply, LinearMap.add_apply, LinearMap.coe_comp, Function.comp_apply, LinearMap.fst_apply, LinearMap.snd_apply, LinearMap.sub_apply, dualProd_apply, polarBilin_apply_apply, prod_apply, neg_apply] simp only [polar_sub_right, polar_self, nsmul_eq_mul, Nat.cast_ofNat, polar_comm _ x.1 x.2, diff --git a/Mathlib/Logic/Function/Defs.lean b/Mathlib/Logic/Function/Defs.lean index 2d7cf78fb0..04b9b63554 100644 --- a/Mathlib/Logic/Function/Defs.lean +++ b/Mathlib/Logic/Function/Defs.lean @@ -31,6 +31,19 @@ def dcomp {β : α → Sort u₂} {φ : ∀ {x : α}, β x → Sort u₃} (f : @[inherit_doc] infixr:80 " ∘' " => Function.dcomp +/-- Product of functions: `Function.prod f g i = (f i, g i)`, where the types of `f i` and +`g i` may depend on `i`. -/ +protected def prod {ι} {α β : ι → Type*} (f : ∀ i, α i) (g : ∀ i, β i) (i : ι) : + α i × β i := (f i, g i) + +@[simp] lemma prod_apply {ι} {α β : ι → Type*} (f : ∀ i, α i) (g : ∀ i, β i) (i : ι) : + Function.prod f g i = (f i , g i) := rfl + +lemma prod_fst_snd {α β} : Function.prod (Prod.fst : α × β → α) (Prod.snd : α × β → β) = id := + rfl +lemma prod_snd_fst {α β} : Function.prod (Prod.snd : α × β → β) (Prod.fst : α × β → α) = .swap := + rfl + /-- Given functions `f : β → β → φ` and `g : α → β`, produce a function `α → α → φ` that evaluates `g` on each argument, then applies `f` to the results. Can be used, e.g., to transfer a relation from `β` to `α`. -/ diff --git a/Mathlib/MeasureTheory/Integral/Prod.lean b/Mathlib/MeasureTheory/Integral/Prod.lean index 6c872f1d9a..7f3f98fb7f 100644 --- a/Mathlib/MeasureTheory/Integral/Prod.lean +++ b/Mathlib/MeasureTheory/Integral/Prod.lean @@ -502,7 +502,7 @@ theorem integral_prod (f : α × β → E) (hf : Integrable f (μ.prod ν)) : measureReal_def, integral_toReal (measurable_measure_prodMk_left hs).aemeasurable (ae_measure_lt_top hs h2s.ne)] - rw [prod_apply hs] + rw [Measure.prod_apply hs] · rintro f g - i_f i_g hf hg simp_rw [integral_add' i_f i_g, integral_integral_add' i_f i_g, hf, hg] · exact isClosed_eq continuous_integral continuous_integral_integral diff --git a/Mathlib/MeasureTheory/Measure/Prod.lean b/Mathlib/MeasureTheory/Measure/Prod.lean index 6ccbdf61d6..e14709c68e 100644 --- a/Mathlib/MeasureTheory/Measure/Prod.lean +++ b/Mathlib/MeasureTheory/Measure/Prod.lean @@ -837,7 +837,7 @@ theorem map_prod_map {δ} [MeasurableSpace δ] {f : α → β} {g : γ → δ} ( -- hence it is placed in the `WithDensity` file, where the instance is defined. lemma prod_smul_left {μ : Measure α} (c : ℝ≥0∞) : (c • μ).prod ν = c • (μ.prod ν) := by ext s hs - rw [Measure.prod_apply hs, Measure.smul_apply, Measure.prod_apply hs] + rw [prod_apply hs, Measure.smul_apply, prod_apply hs] simp end Measure @@ -873,7 +873,7 @@ theorem skew_product [SFinite μa] [SFinite μc] {f : α → β} (hf : MeasurePr infer_instance -- Thus we can use the integral formula for the product measure, and compute things explicitly ext s hs - rw [map_apply this hs, prod_apply (this hs), prod_apply hs, + rw [map_apply this hs, Measure.prod_apply (this hs), Measure.prod_apply hs, ← hf.lintegral_comp (measurable_measure_prodMk_left hs)] apply lintegral_congr_ae filter_upwards [hg] with a ha @@ -898,7 +898,7 @@ theorem prod_of_right {f : α × β → γ} {μ : Measure α} {ν : Measure β} QuasiMeasurePreserving f (μ.prod ν) τ := by refine ⟨hf, ?_⟩ refine AbsolutelyContinuous.mk fun s hs h2s => ?_ - rw [map_apply hf hs, prod_apply (hf hs)]; simp_rw [preimage_preimage] + rw [map_apply hf hs, Measure.prod_apply (hf hs)]; simp_rw [preimage_preimage] rw [lintegral_congr_ae (h2f.mono fun x hx => hx.preimage_null h2s), lintegral_zero] theorem prod_of_left {α β γ} [MeasurableSpace α] [MeasurableSpace β] [MeasurableSpace γ] @@ -1222,8 +1222,9 @@ theorem _root_.MeasureTheory.measurePreserving_prodAssoc (μa : Measure α) (μb have A (x : α) : MeasurableSet (Prod.mk x ⁻¹' s) := measurable_prodMk_left hs have B : MeasurableSet (MeasurableEquiv.prodAssoc ⁻¹' s) := MeasurableEquiv.prodAssoc.measurable hs - simp_rw [map_apply MeasurableEquiv.prodAssoc.measurable hs, prod_apply hs, prod_apply (A _), - prod_apply B, lintegral_prod _ (measurable_measure_prodMk_left B).aemeasurable] + simp_rw [map_apply MeasurableEquiv.prodAssoc.measurable hs, Measure.prod_apply hs, + Measure.prod_apply (A _), Measure.prod_apply B, + lintegral_prod _ (measurable_measure_prodMk_left B).aemeasurable] rfl theorem _root_.MeasureTheory.volume_preserving_prodAssoc {α₁ β₁ γ₁ : Type*} [MeasureSpace α₁] diff --git a/Mathlib/NumberTheory/Height/Basic.lean b/Mathlib/NumberTheory/Height/Basic.lean index f8e6f016da..3e1eb2e95a 100644 --- a/Mathlib/NumberTheory/Height/Basic.lean +++ b/Mathlib/NumberTheory/Height/Basic.lean @@ -667,7 +667,7 @@ lemma mulHeight_fun_prod_eq {x : (a : α) → ι a → K} (hx : ∀ a, x a ≠ 0 simp_rw [ne_iff, Pi.zero_def] at hx ⊢ choose f hf using hx exact ⟨f, prod_ne_zero_iff.mpr fun a _ ↦ hf a⟩ - simp_rw [map_prod, Real.iSup_prod_eq_prod_iSup_of_nonnegHomClass] + simp_rw [_root_.map_prod, Real.iSup_prod_eq_prod_iSup_of_nonnegHomClass] rw [Multiset.prod_map_prod, finprod_prod_comm _ _ fun b _ ↦ hasFiniteMulSupport_iSup_nonarchAbsVal (hx b), ← prod_mul_distrib] diff --git a/Mathlib/SetTheory/Cardinal/Finite.lean b/Mathlib/SetTheory/Cardinal/Finite.lean index a5a8ff1613..9a618a494b 100644 --- a/Mathlib/SetTheory/Cardinal/Finite.lean +++ b/Mathlib/SetTheory/Cardinal/Finite.lean @@ -237,7 +237,7 @@ theorem card_sigma {β : α → Type*} [Fintype α] [∀ a, Finite (β a)] : simp_rw [Nat.card_eq_fintype_card, Fintype.card_sigma] theorem card_pi {β : α → Type*} [Fintype α] : Nat.card (∀ a, β a) = ∏ a, Nat.card (β a) := by - simp_rw [Nat.card, mk_pi, prod_eq_of_fintype, toNat_lift, map_prod] + simp_rw [Nat.card, mk_pi, prod_eq_of_fintype, toNat_lift, _root_.map_prod] theorem card_fun [Finite α] : Nat.card (α → β) = Nat.card β ^ Nat.card α := by haveI := Fintype.ofFinite α diff --git a/Mathlib/Topology/Algebra/ContinuousAffineMap.lean b/Mathlib/Topology/Algebra/ContinuousAffineMap.lean index 8d123a7660..901f4bf7b5 100644 --- a/Mathlib/Topology/Algebra/ContinuousAffineMap.lean +++ b/Mathlib/Topology/Algebra/ContinuousAffineMap.lean @@ -400,7 +400,7 @@ def prod (f : P₁ →ᴬ[k] P₂) (g : P₁ →ᴬ[k] P₃) : P₁ →ᴬ[k] P __ := AffineMap.prod f g cont := by eta_expand; dsimp; fun_prop -theorem coe_prod (f : P₁ →ᴬ[k] P₂) (g : P₁ →ᴬ[k] P₃) : prod f g = Pi.prod f g := +theorem coe_prod (f : P₁ →ᴬ[k] P₂) (g : P₁ →ᴬ[k] P₃) : prod f g = Function.prod f g := rfl @[simp] diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Basic.lean b/Mathlib/Topology/Algebra/InfiniteSum/Basic.lean index 9062eda5f0..c680957af6 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/Basic.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/Basic.lean @@ -221,7 +221,7 @@ protected theorem HasProd.map [CommMonoid γ] [TopologicalSpace γ] (hf : HasPro protected theorem Topology.IsInducing.hasProd_iff [CommMonoid γ] [TopologicalSpace γ] {G} [FunLike G α γ] [MonoidHomClass G α γ] {g : G} (hg : IsInducing g) (f : β → α) (a : α) : HasProd (g ∘ f) (g a) L ↔ HasProd f a L := by - simp_rw [HasProd, comp_apply, ← map_prod] + simp_rw [HasProd, comp_apply, ← _root_.map_prod] exact hg.tendsto_nhds_iff.symm @[to_additive] @@ -261,7 +261,7 @@ lemma Topology.IsClosedEmbedding.map_tprod {ι α α' G : Type*} simp only [Multipliable, HasProd] at h ⊢ obtain ⟨b, hb⟩ := h obtain ⟨a, ha⟩ : b ∈ Set.range g := - hge.isClosed_range.mem_of_tendsto hb (.of_forall <| by simp [← map_prod]) + hge.isClosed_range.mem_of_tendsto hb (.of_forall <| by simp [← _root_.map_prod]) use a simp [hge.tendsto_nhds_iff, Function.comp_def, ha, hb] · simpa [tprod_bot hL] using @@ -288,7 +288,7 @@ lemma Topology.IsInducing.multipliable_iff_tprod_comp_mem_range [CommMonoid γ] · by_cases hL : L.NeBot · exact ⟨_, hf.map_tprod g hg.continuous⟩ · by_cases hfs : (mulSupport fun x ↦ g (f x)).Finite - · simp [tprod_bot hL, finprod_eq_prod _ hfs, ← map_prod] + · simp [tprod_bot hL, finprod_eq_prod _ hfs, ← _root_.map_prod] · exact ⟨1, by simp [tprod_bot hL, finprod_of_infinite_mulSupport hfs]⟩ · rintro ⟨hgf, a, ha⟩ use a diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Constructions.lean b/Mathlib/Topology/Algebra/InfiniteSum/Constructions.lean index 1be2234cb6..0f29cf7e43 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/Constructions.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/Constructions.lean @@ -274,7 +274,7 @@ variable {ι : Type*} {X : α → Type*} [∀ x, CommMonoid (X x)] [∀ x, Topol @[to_additive] theorem Pi.hasProd {f : ι → ∀ x, X x} {g : ∀ x, X x} : HasProd f g L ↔ ∀ x, HasProd (fun i ↦ f i x) (g x) L := by - simp only [HasProd, tendsto_pi_nhds, prod_apply] + simp only [HasProd, tendsto_pi_nhds, Finset.prod_apply] @[to_additive] theorem Pi.multipliable {f : ι → ∀ x, X x} : diff --git a/Mathlib/Topology/Inseparable.lean b/Mathlib/Topology/Inseparable.lean index 956b0bfc78..ec597005b3 100644 --- a/Mathlib/Topology/Inseparable.lean +++ b/Mathlib/Topology/Inseparable.lean @@ -483,7 +483,7 @@ theorem subtype_inseparable_iff {p : X → Prop} (x y : Subtype p) : (x ~ᵢ y) @[simp] theorem inseparable_prod {x₁ x₂ : X} {y₁ y₂ : Y} : ((x₁, y₁) ~ᵢ (x₂, y₂)) ↔ (x₁ ~ᵢ x₂) ∧ (y₁ ~ᵢ y₂) := by - simp only [Inseparable, nhds_prod_eq, prod_inj] + simp only [Inseparable, nhds_prod_eq, Filter.prod_inj] theorem Inseparable.prod {x₁ x₂ : X} {y₁ y₂ : Y} (hx : x₁ ~ᵢ x₂) (hy : y₁ ~ᵢ y₂) : (x₁, y₁) ~ᵢ (x₂, y₂) := diff --git a/Mathlib/Topology/UniformSpace/LocallyUniformConvergence.lean b/Mathlib/Topology/UniformSpace/LocallyUniformConvergence.lean index 2170661fd4..c86814c50b 100644 --- a/Mathlib/Topology/UniformSpace/LocallyUniformConvergence.lean +++ b/Mathlib/Topology/UniformSpace/LocallyUniformConvergence.lean @@ -208,7 +208,7 @@ theorem TendstoLocallyUniformlyOn.prodMk [UniformSpace γ] {G : ι → α → γ theorem TendstoLocallyUniformlyOn.piProd [UniformSpace γ] {G : ι → α → γ} {g : α → γ} (hF : TendstoLocallyUniformlyOn F f p s) (hG : TendstoLocallyUniformlyOn G g p s) : - TendstoLocallyUniformlyOn (fun n ↦ Pi.prod (F n) (G n)) (Pi.prod f g) p s := + TendstoLocallyUniformlyOn (fun n ↦ Function.prod (F n) (G n)) (Function.prod f g) p s := hF.prodMk hG theorem TendstoLocallyUniformly.prodMk [UniformSpace γ] {G : ι → α → γ} {g : α → γ} @@ -219,7 +219,7 @@ theorem TendstoLocallyUniformly.prodMk [UniformSpace γ] {G : ι → α → γ} theorem TendstoLocallyUniformly.piProd [UniformSpace γ] {G : ι → α → γ} {g : α → γ} (hF : TendstoLocallyUniformly F f p) (hG : TendstoLocallyUniformly G g p) : - TendstoLocallyUniformly (fun n ↦ Pi.prod (F n) (G n)) (Pi.prod f g) p := + TendstoLocallyUniformly (fun n ↦ Function.prod (F n) (G n)) (Function.prod f g) p := hF.prodMk hG /-- If every `x ∈ s` has a neighbourhood within `s` on which `F i` tends uniformly to `f`, then From 6927ac912e36db483d661d8ffdc56deab35f8d31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 01:42:43 +0000 Subject: [PATCH 27/65] chore(Order/SuccPred/LinearLocallyFinite): no expose (#39015) The definitions in this file don't give any useful def-eqs, so we opt to not expose them. --- Mathlib/Order/SuccPred/LinearLocallyFinite.lean | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mathlib/Order/SuccPred/LinearLocallyFinite.lean b/Mathlib/Order/SuccPred/LinearLocallyFinite.lean index c2becb30a6..c4ff141f24 100644 --- a/Mathlib/Order/SuccPred/LinearLocallyFinite.lean +++ b/Mathlib/Order/SuccPred/LinearLocallyFinite.lean @@ -55,8 +55,7 @@ About `toZ`: -/ -@[expose] public section - +public section open Order From d9694e37437f9a5cb6f81f8b25c4c754b398e213 Mon Sep 17 00:00:00 2001 From: Thomas Zhu <29544653+hanwenzhu@users.noreply.github.com> Date: Sun, 10 May 2026 05:43:05 +0000 Subject: [PATCH 28/65] chore(Tactic/Translate): remove extra `}` in debug message (#39131) --- Mathlib/Tactic/Translate/Core.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Tactic/Translate/Core.lean b/Mathlib/Tactic/Translate/Core.lean index 36f8e71626..d4bf5141cf 100644 --- a/Mathlib/Tactic/Translate/Core.lean +++ b/Mathlib/Tactic/Translate/Core.lean @@ -302,7 +302,7 @@ where Unless the original translation was wrong, please remove this `{t.attrName}` attribute." modifyEnv (t.translations.addEntry · (src, info)) trace[translate] "Added translation {src} ↦ {tgt}\ - {if info.reorder.reorder.isEmpty then "" else s!" (reorder := {info.reorder.reorder})}"} \ + {if info.reorder.reorder.isEmpty then "" else s!" (reorder := {info.reorder.reorder})"} \ (relevant_arg := {info.relevantArg})" /-- `Config` is the type of the arguments that can be provided to `to_additive`. -/ From fdcab2e13feb4b2e61233d3a7f09fd62ccbf08aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 07:45:19 +0000 Subject: [PATCH 29/65] chore(SetTheory): turn `#Ordinal = univ` around (#38365) --- Mathlib/SetTheory/Ordinal/Univ.lean | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Mathlib/SetTheory/Ordinal/Univ.lean b/Mathlib/SetTheory/Ordinal/Univ.lean index 3b881c094b..fdb00da8c7 100644 --- a/Mathlib/SetTheory/Ordinal/Univ.lean +++ b/Mathlib/SetTheory/Ordinal/Univ.lean @@ -114,6 +114,11 @@ end Ordinal namespace Cardinal +@[simp] +theorem mk_ordinal : #Ordinal = univ.{u, u + 1} := + (lift_id _).symm + +@[deprecated mk_ordinal (since := "2026-04-22")] theorem univ_id : univ.{u, u + 1} = #Ordinal := lift_id _ @@ -168,7 +173,7 @@ theorem lt_univ {c} : c < univ.{u, u + 1} ↔ ∃ c', c = lift.{u + 1, u} c' := theorem lt_univ' {c} : c < univ.{u, v} ↔ ∃ c', c = lift.{max (u + 1) v, u} c' := ⟨fun h => by let ⟨a, h', e⟩ := lt_lift_iff.1 h - rw [← univ_id] at h' + rw [mk_ordinal] at h' rcases lt_univ.{u}.1 h' with ⟨c', rfl⟩ exact ⟨c', by simp only [e.symm, lift_lift]⟩, fun ⟨_, e⟩ => e.symm ▸ lift_lt_univ' _⟩ From b9413917cb29d1c75ca094d337d74fc44f440c3b Mon Sep 17 00:00:00 2001 From: Sebastien Gouezel <10818434+sgouezel@users.noreply.github.com> Date: Sun, 10 May 2026 08:13:38 +0000 Subject: [PATCH 30/65] chore: remove set_option in Analysis.Analytic.Composition (#38929) Co-authored-by: sgouezel --- Mathlib/Analysis/Analytic/Composition.lean | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Mathlib/Analysis/Analytic/Composition.lean b/Mathlib/Analysis/Analytic/Composition.lean index 57015f27b5..e67f66be85 100644 --- a/Mathlib/Analysis/Analytic/Composition.lean +++ b/Mathlib/Analysis/Analytic/Composition.lean @@ -115,7 +115,6 @@ theorem applyComposition_ones (p : FormalMultilinearSeries 𝕜 E F) (n : ℕ) : refine congr_arg v ?_ rw [Fin.ext_iff, Fin.val_castLE, Composition.ones_embedding, Fin.val_mk] -set_option backward.isDefEq.respectTransparency false in theorem applyComposition_single (p : FormalMultilinearSeries 𝕜 E F) {n : ℕ} (hn : 0 < n) (v : Fin n → E) : p.applyComposition (Composition.single n hn) v = fun _j => p n v := by ext j @@ -125,8 +124,8 @@ theorem applyComposition_single (p : FormalMultilinearSeries 𝕜 E F) {n : ℕ} convert Composition.single_embedding hn ⟨i, hi2⟩ using 1 obtain ⟨j_val, j_property⟩ := j have : j_val = 0 := le_bot_iff.1 (Nat.lt_succ_iff.1 j_property) - congr! - simp + rw! [this] + rfl @[simp] theorem removeZero_applyComposition (p : FormalMultilinearSeries 𝕜 E F) {n : ℕ} @@ -1253,7 +1252,6 @@ namespace FormalMultilinearSeries open Composition -set_option backward.isDefEq.respectTransparency false in theorem comp_assoc (r : FormalMultilinearSeries 𝕜 G H) (q : FormalMultilinearSeries 𝕜 F G) (p : FormalMultilinearSeries 𝕜 E F) : (r.comp q).comp p = r.comp (q.comp p) := by ext n v @@ -1292,6 +1290,7 @@ theorem comp_assoc (r : FormalMultilinearSeries 𝕜 G H) (q : FormalMultilinear -- `sizeUpTo_sizeUpTo_add`. refine congr_arg v (Fin.ext ?_) dsimp [Composition.embedding] - rw [sizeUpTo_sizeUpTo_add _ _ hi1 hj1, add_assoc] + rw [← add_assoc, ← sizeUpTo_sizeUpTo_add _ _ hi1 hj1] + rfl end FormalMultilinearSeries From c31ea4afd98ad74369041351478cd799657ae395 Mon Sep 17 00:00:00 2001 From: Thomas Browning <13339017+tb65536@users.noreply.github.com> Date: Sun, 10 May 2026 08:13:40 +0000 Subject: [PATCH 31/65] chore(RepresentationTheory/Character): allow field and group to have different universes (#39022) This PR refactors some of the representation theory library to allow the ring/field and the group to lie in different universes. Co-authored-by: tb65536 --- Mathlib/RepresentationTheory/Character.lean | 8 ++++---- Mathlib/RepresentationTheory/FDRep.lean | 10 +++++----- Mathlib/RepresentationTheory/Invariants.lean | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Mathlib/RepresentationTheory/Character.lean b/Mathlib/RepresentationTheory/Character.lean index 5a4bb405d6..d9173aa051 100644 --- a/Mathlib/RepresentationTheory/Character.lean +++ b/Mathlib/RepresentationTheory/Character.lean @@ -37,7 +37,7 @@ defined in `Mathlib/CategoryTheory/Simple.lean` noncomputable section -universe u +universe u v open CategoryTheory LinearMap CategoryTheory.MonoidalCategory Representation Module @@ -47,7 +47,7 @@ namespace FDRep section Monoid -variable {G : Type u} [Monoid G] +variable {G : Type v} [Monoid G] /-- The character of a representation `V : FDRep k G` is the function associating to `g : G` the trace of the linear map `V.ρ g`. -/ @@ -76,7 +76,7 @@ end Monoid section Group -variable {G : Type u} [Group G] +variable {G : Type v} [Group G] /-- The character of a representation is constant on conjugacy classes. -/ @[simp] @@ -119,7 +119,7 @@ end Group section Orthogonality -variable {G : Type u} [Group G] [IsAlgClosed k] +variable {G : Type v} [Group G] [IsAlgClosed k] variable [Fintype G] [Invertible (Fintype.card G : k)] diff --git a/Mathlib/RepresentationTheory/FDRep.lean b/Mathlib/RepresentationTheory/FDRep.lean index 4b94c3c460..bdfcace5ec 100644 --- a/Mathlib/RepresentationTheory/FDRep.lean +++ b/Mathlib/RepresentationTheory/FDRep.lean @@ -50,7 +50,7 @@ and this is reflected in the documentation. suppress_compilation -universe u +universe u v open CategoryTheory @@ -61,14 +61,14 @@ open CategoryTheory.Limits Note that `R` can be any ring, but the main case of interest is when `R = k` is a field and `G` is a group. -/ -abbrev FDRep (R G : Type u) [Ring R] [Monoid G] := +abbrev FDRep (R : Type u) (G : Type v) [Ring R] [Monoid G] := Action (FGModuleCat.{u} R) G namespace FDRep -variable {R k G : Type u} [CommRing R] [Field k] [Monoid G] +variable {R k : Type u} {G : Type v} [CommRing R] [Field k] [Monoid G] -example : LargeCategory (FDRep R G) := by infer_instance +example {G : Type u} [Monoid G] : LargeCategory (FDRep R G) := by infer_instance example : ConcreteCategory (FDRep R G) (Action.HomSubtype _ _) := by infer_instance example : Preadditive (FDRep R G) := by infer_instance example : HasFiniteLimits (FDRep k G) := by infer_instance @@ -195,7 +195,7 @@ namespace FDRep -- below should then just be obtained from general results about rigid categories. open Representation -variable {k G V : Type u} [Field k] [Group G] +variable {k : Type u} {G : Type v} {V : Type u} [Field k] [Group G] variable [AddCommGroup V] [Module k V] variable [FiniteDimensional k V] variable (ρV : Representation k G V) (W : FDRep k G) diff --git a/Mathlib/RepresentationTheory/Invariants.lean b/Mathlib/RepresentationTheory/Invariants.lean index d499a56240..73f0aa71d5 100644 --- a/Mathlib/RepresentationTheory/Invariants.lean +++ b/Mathlib/RepresentationTheory/Invariants.lean @@ -211,7 +211,7 @@ end Rep section FDRep -variable {k : Type u} [Field k] {G : Type u} [Group G] +variable {k : Type u} [Field k] {G : Type v} [Group G] /-- The invariants of the representation `linHom X.ρ Y.ρ` correspond to the representation homomorphisms from `X` to `Y`. -/ From 34d82a7cc54f0d0e8ba2ac3adff968c3806c3ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 08:13:42 +0000 Subject: [PATCH 32/65] chore: fix accidental namespace (#39135) These lemmas were added only 5 days ago in #38809, so I didn't bother with deprecations. --- Mathlib/Order/DirSupClosed.lean | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mathlib/Order/DirSupClosed.lean b/Mathlib/Order/DirSupClosed.lean index 56faba3e38..521e2cc330 100644 --- a/Mathlib/Order/DirSupClosed.lean +++ b/Mathlib/Order/DirSupClosed.lean @@ -221,7 +221,7 @@ theorem dirSupClosedOn_singleton (a : α) : DirSupClosedOn D {a} := end PartialOrder -namespace LinearOrder +section LinearOrder variable [LinearOrder α] theorem dirSupClosedOn_iff_of_linearOrder : From ec65f5a4ff0cdb1ca212961ca4a30bd917ded033 Mon Sep 17 00:00:00 2001 From: HerrLaal <77057945+HerrLaal@users.noreply.github.com> Date: Sun, 10 May 2026 08:44:10 +0000 Subject: [PATCH 33/65] doc: fix misleading docstrings (#39121) Fix a typo and update an outdated docstring. --- Mathlib/Tactic/GCongr/Core.lean | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Mathlib/Tactic/GCongr/Core.lean b/Mathlib/Tactic/GCongr/Core.lean index 725b16cfbb..419d3b8035 100644 --- a/Mathlib/Tactic/GCongr/Core.lean +++ b/Mathlib/Tactic/GCongr/Core.lean @@ -228,7 +228,7 @@ def getRel (e : Expr) : Option (Name × Expr × Expr) := none | _ => none -/-- If `e` is of the form `r a b`, replace either `a` or `b` with `e`. -/ +/-- If `r` is of the form `rel a b`, replace either `a` or `b` with `e`. -/ def updateRel (r e : Expr) (isLhs : Bool) : Expr := match r with | .forallE _ d b _ => if isLhs then r.updateForallE! e b else r.updateForallE! d e @@ -517,7 +517,7 @@ lemma rel_imp_rel (h₁ : r c a) (h₂ : r b d) : r a b → r c d := Construct a `GCongrLemma` for `gcongr` goals of the form `a ≺ b → c ≺ d`. This will be tried if there is no other available `@[gcongr]` lemma. For example, the relation `a ≡ b [ZMOD n]` has an instance of `IsTrans`, so a congruence of the form -`a ≡ b [ZMOD n] → c ≡ d [ZMOD n]` can be solved with `rel_imp_rel`, `rel_trans` or `rel_trans'`. +`a ≡ b [ZMOD n] → c ≡ d [ZMOD n]` can be solved with `rel_imp_rel`. -/ def relImpRelLemma (arity : Nat) : List GCongrLemma := if arity < 2 then [] else [{ From 835f8a39246bdb32c07a80ef2b8ec505298e718b Mon Sep 17 00:00:00 2001 From: Nailin Guan <150537269+Thmoas-Guan@users.noreply.github.com> Date: Sun, 10 May 2026 09:03:04 +0000 Subject: [PATCH 34/65] feat(RingTheory): refactor `smulShortComplex` (#37355) Use `LinearMap.lsmul` for the `f` of `ModuleCat.smulShortComplex`, also providing new APIs for it. --- Mathlib/RingTheory/Regular/Category.lean | 39 +++++++++++------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/Mathlib/RingTheory/Regular/Category.lean b/Mathlib/RingTheory/Regular/Category.lean index e27bfc5440..f81fe48953 100644 --- a/Mathlib/RingTheory/Regular/Category.lean +++ b/Mathlib/RingTheory/Regular/Category.lean @@ -6,7 +6,7 @@ Authors: Jingting Wang, Wanyi He, Nailin Guan module public import Mathlib.Algebra.Homology.ShortComplex.ModuleCat -public import Mathlib.RingTheory.QuotSMulTop +public import Mathlib.Algebra.Module.Submodule.Pointwise /-! # Categorical constructions for `IsSMulRegular` @@ -18,32 +18,29 @@ universe u v w variable {R : Type u} [CommRing R] (M : ModuleCat.{v} R) -open CategoryTheory Ideal Pointwise +open CategoryTheory Abelian Pointwise -lemma LinearMap.exact_smul_id_smul_top_mkQ (M : Type v) [AddCommGroup M] [Module R M] (r : R) : - Function.Exact (r • LinearMap.id : M →ₗ[R] M) (r • (⊤ : Submodule R M)).mkQ := by +lemma LinearMap.exact_lsmul_mkQ_smul_top (M : Type v) [AddCommGroup M] [Module R M] (r : R) : + Function.Exact (LinearMap.lsmul _ M r) (r • (⊤ : Submodule R M)).mkQ := by intro x - simp [Submodule.mem_smul_pointwise_iff_exists, - Submodule.mem_smul_pointwise_iff_exists] + simp [Submodule.mem_smul_pointwise_iff_exists, Submodule.mem_smul_pointwise_iff_exists] + +@[deprecated (since := "2026-04-13")] +alias LinearMap.exact_smul_id_smul_top_mkQ := LinearMap.exact_lsmul_mkQ_smul_top namespace ModuleCat /-- The short (exact) complex `M → M → M⧸xM` obtain from the scalar multiple of `x : R` on `M`. -/ -@[simps] -def smulShortComplex (r : R) : - ShortComplex (ModuleCat R) where - X₁ := M - X₂ := M - X₃ := ModuleCat.of R (QuotSMulTop r M) - f := ModuleCat.ofHom (r • LinearMap.id) - g := ModuleCat.ofHom (r • (⊤ : Submodule R M)).mkQ - zero := by - ext x - exact (LinearMap.exact_smul_id_smul_top_mkQ M r).apply_apply_eq_zero x - -lemma smulShortComplex_exact (r : R) : (smulShortComplex M r).Exact := by - simp [smulShortComplex, ShortComplex.ShortExact.moduleCat_exact_iff_function_exact, - LinearMap.exact_smul_id_smul_top_mkQ, -LinearMap.coe_smul] +@[simps!] +def smulShortComplex (r : R) : ShortComplex (ModuleCat R) := + ModuleCat.shortComplexOfCompEqZero (LinearMap.lsmul _ M r) (r • (⊤ : Submodule R M)).mkQ + (LinearMap.exact_lsmul_mkQ_smul_top M r).linearMap_comp_eq_zero + +@[simp] +lemma smulShortComplex_f_eq_smul_id (r : R) : (M.smulShortComplex r).f = r • 𝟙 M := rfl + +lemma smulShortComplex_exact (r : R) : (smulShortComplex M r).Exact := + ModuleCat.shortComplex_exact _ (LinearMap.exact_lsmul_mkQ_smul_top M r) instance smulShortComplex_g_epi (r : R) : Epi (smulShortComplex M r).g := by simpa [smulShortComplex, ModuleCat.epi_iff_surjective] using Submodule.mkQ_surjective _ From e0b7a966346d8fc21bfeacf171aef1a0a801f264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 09:03:06 +0000 Subject: [PATCH 35/65] =?UTF-8?q?feat:=20supremum=20of=20`=E2=89=A4=20c`?= =?UTF-8?q?=20ordinals=20of=20cardinal=20`=E2=89=A4=20c`=20has=20cardinal?= =?UTF-8?q?=20`=E2=89=A4=20c`=20(#37573)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mathlib/SetTheory/Cardinal/Ordinal.lean | 60 +++++++++++++++++++------ 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/Mathlib/SetTheory/Cardinal/Ordinal.lean b/Mathlib/SetTheory/Cardinal/Ordinal.lean index a005c37126..bc8955f159 100644 --- a/Mathlib/SetTheory/Cardinal/Ordinal.lean +++ b/Mathlib/SetTheory/Cardinal/Ordinal.lean @@ -53,19 +53,21 @@ end Cardinal namespace Ordinal -theorem lift_card_iSup_le_sum_card {ι : Type u} [Small.{v} ι] (f : ι → Ordinal.{v}) : +theorem lift_card_iSup_le_sum_card {ι : Type u} (f : ι → Ordinal.{v}) : Cardinal.lift.{u} (⨆ i, f i).card ≤ Cardinal.sum fun i ↦ (f i).card := by + by_cases! hf : ¬ BddAbove (range f) + · simp [ciSup_of_not_bddAbove hf] simp_rw [← mk_toType] rw [← mk_sigma, ← Cardinal.lift_id'.{v} #(Σ _, _), ← Cardinal.lift_umax.{v, u}] apply lift_mk_le_lift_mk_of_surjective (f := .mk ∘ (⟨·.2.toOrd, - (mem_Iio.mp (ToType.toOrd _).2).trans_le (Ordinal.le_iSup _ _)⟩)) + (mem_Iio.mp (ToType.toOrd _).2).trans_le (le_ciSup hf _)⟩)) rw [EquivLike.comp_surjective] rintro ⟨x, hx⟩ - obtain ⟨i, hi⟩ := Ordinal.lt_iSup_iff.mp hx + obtain ⟨i, hi⟩ := (lt_ciSup_iff' hf).mp hx exact ⟨⟨i, .mk ⟨x, hi⟩⟩, by simp⟩ theorem card_iSup_le_sum_card {ι : Type u} (f : ι → Ordinal.{max u v}) : - (⨆ i, f i).card ≤ Cardinal.sum (fun i ↦ (f i).card) := by + (⨆ i, f i).card ≤ Cardinal.sum fun i ↦ (f i).card := by have := lift_card_iSup_le_sum_card f rwa [Cardinal.lift_id'] at this @@ -81,6 +83,45 @@ theorem card_iSup_Iio_le_card_mul_iSup {o : Ordinal.{u}} (f : Iio o → Ordinal. · exact mk_toType o · exact ToType.mk.symm.iSup_comp (g := fun x ↦ (f x).card) +theorem card_iSup_le_lift {ι : Type u} {c : Cardinal} {f : ι → Ordinal.{v}} + (hι : Cardinal.lift.{v} #ι ≤ Cardinal.lift.{u} c) (hf : ∀ i, (f i).card ≤ c) : + (⨆ i, f i).card ≤ c := by + by_cases! hc : c < ℵ₀ + · obtain ⟨n, rfl⟩ := lt_aleph0.1 hc + rw [card_le_nat] + refine ciSup_le' fun i ↦ ?_ + simpa using hf i + · rw [← Cardinal.lift_le.{u}] + apply (lift_card_iSup_le_sum_card ..).trans ((sum_le_lift_mk_mul_iSup_lift _).trans _) + rw [← mul_eq_self hc, Cardinal.lift_mul] + apply mul_le_mul' hι (ciSup_le' _) + simpa [← lift_card] + +theorem card_iSup_le {ι : Type*} {c : Cardinal} {f : ι → Ordinal} + (hι : #ι ≤ c) (hf : ∀ i, (f i).card ≤ c) : (⨆ i, f i).card ≤ c := by + rw [← Cardinal.lift_le] at hι + simpa using card_iSup_le_lift hι hf + +theorem card_iSup_Iio_le_of_lift {o : Ordinal.{u}} {c : Cardinal} {f : Iio o → Ordinal.{v}} + (hι : Cardinal.lift.{v} o.card ≤ Cardinal.lift.{u} c) (hf : ∀ i, (f i).card ≤ c) : + (⨆ i, f i).card ≤ c := by + apply card_iSup_le_lift _ hf + conv_rhs => rw [← Cardinal.lift_lift.{u, u + 1}] + rwa [Cardinal.mk_Iio_ordinal, Cardinal.lift_lift, ← Cardinal.lift_lift.{v, u + 1}, + Cardinal.lift_le] + +theorem card_iSup_Iio_le {o : Ordinal} {c : Cardinal} {f : Iio o → Ordinal} + (hι : o.card ≤ c) (hf : ∀ i, (f i).card ≤ c) : (⨆ i, f i).card ≤ c := by + rw [← Cardinal.lift_le] at hι + simpa using card_iSup_Iio_le_of_lift hι hf + +theorem card_sSup_le {c : Cardinal} {s : Set Ordinal.{u}} + (hs : #s ≤ Cardinal.lift.{u + 1} c) (hs' : ∀ x ∈ s, x.card ≤ c) : (sSup s).card ≤ c := by + rw [sSup_eq_iSup'] + apply card_iSup_le_lift + · rwa [Cardinal.lift_id'.{u, u + 1}] + · simpa + theorem card_opow_le_of_omega0_le_left {a : Ordinal} (ha : ω ≤ a) (b : Ordinal) : (a ^ b).card ≤ max a.card b.card := by refine limitRecOn b ?_ ?_ ?_ @@ -97,15 +138,8 @@ theorem card_opow_le_of_omega0_le_left {a : Ordinal} (ha : ω ≤ a) (b : Ordina · rwa [aleph0_le_card] · intro b hb IH rw [(isNormal_opow (one_lt_omega0.trans_le ha)).apply_of_isSuccLimit hb] - apply (card_iSup_Iio_le_card_mul_iSup _).trans - rw [Cardinal.lift_id, Cardinal.mul_eq_max_of_aleph0_le_right, max_comm] - · apply max_le _ (le_max_right _ _) - apply ciSup_le' - rintro ⟨c, (hcb : c < b)⟩ - grw [IH c hcb, hcb] - · simpa using hb.ne_bot - · exact le_ciSup_of_le Cardinal.bddAbove_of_small - ⟨1, one_lt_omega0.trans_le <| omega0_le_of_isSuccLimit hb⟩ (by simpa) + exact card_iSup_Iio_le (le_max_right ..) fun i ↦ + (IH i i.2).trans (max_le_max_left _ (card_le_card i.2.le)) theorem card_opow_le_of_omega0_le_right (a : Ordinal) {b : Ordinal} (hb : ω ≤ b) : (a ^ b).card ≤ max a.card b.card := by From 03fe349eb1f7c7f75cbfca8289ab530bc78fdfdd Mon Sep 17 00:00:00 2001 From: Thomas Browning <13339017+tb65536@users.noreply.github.com> Date: Sun, 10 May 2026 09:03:08 +0000 Subject: [PATCH 36/65] feat(RingTheory/LocalRing/ResidueField/Fiber): `Ideal.Fiber` is a quotient of a localization (#37858) This PR proves that `Ideal.Fiber` is a quotient of a localization. This is needed for #37130. Co-authored-by: tb65536 --- .../RingTheory/LocalRing/ResidueField/Fiber.lean | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Fiber.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Fiber.lean index 21c2586192..dae55a28f2 100644 --- a/Mathlib/RingTheory/LocalRing/ResidueField/Fiber.lean +++ b/Mathlib/RingTheory/LocalRing/ResidueField/Fiber.lean @@ -72,6 +72,22 @@ lemma Ideal.Fiber.exists_smul_eq_one_tmul (x : p.Fiber S) : ∃ r ∉ p, ∃ s, (Algebra.TensorProduct.comm _ _ _ x) refine ⟨r, hr, s, by simpa using congr((Algebra.TensorProduct.comm _ _ _).symm $e)⟩ +attribute [local instance] Algebra.TensorProduct.rightAlgebra in +/-- `p.Fiber S` is isomorphic to the quotient `Sₚ ⧸ pSₚ`. -/ +noncomputable def Fiber.algEquivQuotient : + letI Rp := Localization p.primeCompl + letI pRp := IsLocalRing.maximalIdeal Rp + letI Sp := Localization (Algebra.algebraMapSubmonoid S p.primeCompl) + letI pSp := pRp.map (algebraMap Rp Sp) + p.Fiber S ≃ₐ[S] Sp ⧸ pSp := + (commRight R S p.ResidueField).symm.trans <| (tensorQuotientEquiv S _ S _).trans <| + { __ := Ideal.quotientEquiv _ _ (Localization.tensorLeftAlgEquiv p.primeCompl S) (by + rw [← Ideal.map_coe includeRight, Ideal.map_map] + congr + ext + simp [Localization.tensorLeftAlgEquiv_apply_one_tmul p.primeCompl]) + commutes' := by simp } + set_option backward.isDefEq.respectTransparency false in variable (R S) in /-- The fiber `PrimeSpectrum S → PrimeSpectrum R` at a prime ideal From dc386040a6979553db874359acbd870fc083b1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 09:17:37 +0000 Subject: [PATCH 37/65] feat(CategoryTheory/Monoidal): Yoneda embedding of ring objects (#36913) (This series of PR mostly builds on previous work by the Toric project.) --- Mathlib.lean | 1 + .../Monoidal/Cartesian/Ring.lean | 83 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 Mathlib/CategoryTheory/Monoidal/Cartesian/Ring.lean diff --git a/Mathlib.lean b/Mathlib.lean index 9f561a4db8..7a8cfab5a0 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -3039,6 +3039,7 @@ public import Mathlib.CategoryTheory.Monoidal.Cartesian.Mon public import Mathlib.CategoryTheory.Monoidal.Cartesian.Mon_ public import Mathlib.CategoryTheory.Monoidal.Cartesian.Normal public import Mathlib.CategoryTheory.Monoidal.Cartesian.Over +public import Mathlib.CategoryTheory.Monoidal.Cartesian.Ring public import Mathlib.CategoryTheory.Monoidal.Cartesian.ShrinkYoneda public import Mathlib.CategoryTheory.Monoidal.Category public import Mathlib.CategoryTheory.Monoidal.Center diff --git a/Mathlib/CategoryTheory/Monoidal/Cartesian/Ring.lean b/Mathlib/CategoryTheory/Monoidal/Cartesian/Ring.lean new file mode 100644 index 0000000000..252ae59a45 --- /dev/null +++ b/Mathlib/CategoryTheory/Monoidal/Cartesian/Ring.lean @@ -0,0 +1,83 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Category.Ring.Basic +public import Mathlib.CategoryTheory.Monoidal.Ring + +/-! +# Yoneda embedding of `RingCatObj C` + +-/ + +@[expose] public section + +open CategoryTheory MonObj + +universe v u + +namespace CategoryTheory + +variable {C : Type u} [Category.{v} C] [CartesianMonoidalCategory C] [BraidedCategory C] + +open scoped CommRingObj RingObj + +/-- If `R` is a ring object, then `Hom(-, R)` is a presheaf of rings. -/ +@[simps! obj] +def yonedaRingObj (R : C) [RingObj R] : Cᵒᵖ ⥤ RingCat.{v} where + obj X := .of (X.unop ⟶ R) + map f := RingCat.ofHom + { toFun x := f.unop ≫ x + map_one' := by simp + map_zero' := by simp + map_mul' _ _ := MonObj.comp_mul _ _ _ + map_add' _ _ := AddMonObj.comp_add _ _ _ } + +@[simp] +lemma yonedaRingObj_map_apply {R : C} [RingObj R] {X Y : Cᵒᵖ} (f : X ⟶ Y) (x : X.unop ⟶ R) : + dsimp% (yonedaRingObj R).map f x = f.unop ≫ x := rfl + +set_option backward.isDefEq.respectTransparency false in +/-- The yoneda embedding of `RingObjCat C` into presheaves of rings. -/ +def yonedaRing : RingObjCat C ⥤ Cᵒᵖ ⥤ RingCat.{v} where + obj R := yonedaRingObj R.X + map f := + { app X := RingCat.ofHom + { toFun x := x ≫ f.hom + map_one' := by simp + map_zero' := by simp + map_mul' _ _ := MonObj.mul_comp _ _ _ + map_add' _ _ := AddMonObj.add_comp _ _ _ } } + +/-- If `R` is a commutative ring object, then `Hom(-, R)` is a presheaf of commutative rings. -/ +@[simps obj] +def yonedaCommRingObj (R : C) [CommRingObj R] : Cᵒᵖ ⥤ CommRingCat.{v} where + obj X := .of (X.unop ⟶ R) + map f := CommRingCat.ofHom ((yonedaRingObj R).map f).hom + +@[simp] +lemma yonedaCommRingObj_map_apply {R : C} [CommRingObj R] {X Y : Cᵒᵖ} (f : X ⟶ Y) (x : X.unop ⟶ R) : + dsimp% (yonedaCommRingObj R).map f x = f.unop ≫ x := rfl + +set_option backward.isDefEq.respectTransparency false in +/-- The yoneda embedding of `CommRingObjCat C` into presheaves of commutative rings. -/ +@[simps obj] +def yonedaCommRing : CommRingObjCat C ⥤ Cᵒᵖ ⥤ CommRingCat.{v} where + obj R := yonedaCommRingObj R.X + map f := + { app X := CommRingCat.ofHom + { toFun x := x ≫ f.hom + map_one' := by simp + map_zero' := by simp + map_mul' _ _ := MonObj.mul_comp _ _ _ + map_add' _ _ := AddMonObj.add_comp _ _ _ } } + +@[simp] +lemma yonedaCommRing_map_app_apply {R₁ R₂ : CommRingObjCat C} (f : R₁ ⟶ R₂) + {X : C} (x : X ⟶ R₁.X) : + dsimp% (yonedaCommRing.map f).app _ x = x ≫ f.hom := rfl + +end CategoryTheory From c05f2e2d85989dd61a802e3101d0dac97aba8e66 Mon Sep 17 00:00:00 2001 From: Robin Carlier <57142648+robin-carlier@users.noreply.github.com> Date: Sun, 10 May 2026 09:17:40 +0000 Subject: [PATCH 38/65] chore(Algebra): remove simps projections for structures of bundled objects (#38005) I noticed that currently, adding a `@[simps]` tag to a `ModuleCat`-valued definition will generate projections for the `isModule` fields. These projections are removed. Same in `BialgCat` and `HopfAlgCat`. --- Mathlib/Algebra/Category/BialgCat/Basic.lean | 1 + Mathlib/Algebra/Category/HopfAlgCat/Basic.lean | 1 + Mathlib/Algebra/Category/ModuleCat/Basic.lean | 1 + Mathlib/Algebra/Category/ModuleCat/Semi.lean | 1 + 4 files changed, 4 insertions(+) diff --git a/Mathlib/Algebra/Category/BialgCat/Basic.lean b/Mathlib/Algebra/Category/BialgCat/Basic.lean index b8985f97d7..f23b362003 100644 --- a/Mathlib/Algebra/Category/BialgCat/Basic.lean +++ b/Mathlib/Algebra/Category/BialgCat/Basic.lean @@ -34,6 +34,7 @@ structure BialgCat where [instRing : Ring carrier] [instBialgebra : Bialgebra R carrier] +initialize_simps_projections BialgCat (-instRing, -instBialgebra) attribute [instance] BialgCat.instBialgebra BialgCat.instRing variable {R} diff --git a/Mathlib/Algebra/Category/HopfAlgCat/Basic.lean b/Mathlib/Algebra/Category/HopfAlgCat/Basic.lean index 1637b25bca..9b15fb679a 100644 --- a/Mathlib/Algebra/Category/HopfAlgCat/Basic.lean +++ b/Mathlib/Algebra/Category/HopfAlgCat/Basic.lean @@ -35,6 +35,7 @@ structure HopfAlgCat where [instRing : Ring carrier] [instHopfAlgebra : HopfAlgebra R carrier] +initialize_simps_projections HopfAlgCat (-instRing, -instHopfAlgebra) attribute [instance] HopfAlgCat.instHopfAlgebra HopfAlgCat.instRing variable {R} diff --git a/Mathlib/Algebra/Category/ModuleCat/Basic.lean b/Mathlib/Algebra/Category/ModuleCat/Basic.lean index dca2a34dfc..d4d14bdef1 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Basic.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Basic.lean @@ -60,6 +60,7 @@ structure ModuleCat where [isAddCommGroup : AddCommGroup carrier] [isModule : Module R carrier] +initialize_simps_projections ModuleCat (-isModule, -isAddCommGroup) attribute [instance] ModuleCat.isAddCommGroup attribute [instance 1100] ModuleCat.isModule diff --git a/Mathlib/Algebra/Category/ModuleCat/Semi.lean b/Mathlib/Algebra/Category/ModuleCat/Semi.lean index 0e9b0a944f..951b0e0834 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Semi.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Semi.lean @@ -59,6 +59,7 @@ structure SemimoduleCat where [isAddCommMonoid : AddCommMonoid carrier] [isModule : Module R carrier] +initialize_simps_projections SemimoduleCat (-isModule, -isAddCommMonoid) attribute [instance] SemimoduleCat.isAddCommMonoid SemimoduleCat.isModule namespace SemimoduleCat From 6cf2883d77ff1b3ae33bffa0fb0780f06d21b05e Mon Sep 17 00:00:00 2001 From: pedro cortes <65571136+pedroscortes@users.noreply.github.com> Date: Sun, 10 May 2026 09:17:42 +0000 Subject: [PATCH 39/65] =?UTF-8?q?feat(CategoryTheory/Monoidal):=20tensor?= =?UTF-8?q?=CE=BC=5Fbraid=5Fswap=20and=20tensor-product=20IsCommComonObj?= =?UTF-8?q?=20(#38314)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Two symmetric-monoidal coherence results: 1. `MonoidalCategory.tensorμ_braid_swap` (in `Monoidal/Braided/Basic.lean`) — canonical rearrangement `tensorμ A A Y Y` intertwines the braiding on `A ⊗ Y` with the pair of braidings on `A` and `Y`. Sibling of `CategoryTheory.MonObj.mul_braiding`. 2. Tensor-product instance for `IsCommComonObj` (in `Monoidal/CommComon_.lean`): if `A, B` carry commutative comonoid structures in a symmetric monoidal category, so does `A ⊗ B`. Fills a gap alongside the existing `instCommComonObjUnit` and `instIsCommComonObjOfCartesian`. ## Downstream consumer The `IsCommComonObj` tensor-product instance is load-bearing for an external library (markovcat, formalising Fritz–Klingler Markov categories). Co-authored-by: Pedro Cortes --- Mathlib/CategoryTheory/Monoidal/Braided/Basic.lean | 8 ++++++++ Mathlib/CategoryTheory/Monoidal/CommComon_.lean | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/Mathlib/CategoryTheory/Monoidal/Braided/Basic.lean b/Mathlib/CategoryTheory/Monoidal/Braided/Basic.lean index 735c177d7f..3066c10a85 100644 --- a/Mathlib/CategoryTheory/Monoidal/Braided/Basic.lean +++ b/Mathlib/CategoryTheory/Monoidal/Braided/Basic.lean @@ -779,6 +779,14 @@ end Tensor end MonoidalCategory +@[reassoc] +theorem SymmetricCategory.tensorμ_braid_swap + {C : Type*} [Category* C] [MonoidalCategory C] [SymmetricCategory C] + (X Y : C) : + tensorμ X X Y Y ≫ (β_ (X ⊗ Y) (X ⊗ Y)).hom = + ((β_ X X).hom ⊗ₘ (β_ Y Y).hom) ≫ tensorμ X X Y Y := by + simp [tensorμ, SymmetricCategory.braiding_swap_eq_inv_braiding Y X, tensorHom_def] + instance : BraidedCategory Cᵒᵖ where braiding X Y := (β_ Y.unop X.unop).op braiding_naturality_right X {_ _} f := Quiver.Hom.unop_inj <| by simp diff --git a/Mathlib/CategoryTheory/Monoidal/CommComon_.lean b/Mathlib/CategoryTheory/Monoidal/CommComon_.lean index 3969d39b1d..7df8792ebb 100644 --- a/Mathlib/CategoryTheory/Monoidal/CommComon_.lean +++ b/Mathlib/CategoryTheory/Monoidal/CommComon_.lean @@ -98,4 +98,11 @@ end end CommComon +instance {C : Type*} [Category* C] [MonoidalCategory C] [SymmetricCategory C] + (A B : C) [ComonObj A] [ComonObj B] + [IsCommComonObj A] [IsCommComonObj B] : IsCommComonObj (A ⊗ B) where + comul_comm := by + rw [Comon.tensorObj_comul, Category.assoc, SymmetricCategory.tensorμ_braid_swap] + simp + end CategoryTheory From ed395cbeaa6fc32bc655663a50852ccacb77a628 Mon Sep 17 00:00:00 2001 From: Eric Wieser <425260+eric-wieser@users.noreply.github.com> Date: Sun, 10 May 2026 09:17:44 +0000 Subject: [PATCH 40/65] feat: `IsEquiv` is equivalent to `Equivalence` (#38827) Ideally we would have only one spelling of this, but in the meantime we should at least have a lemma to transport between the two spellings. --- Mathlib/Order/Defs/Unbundled.lean | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Mathlib/Order/Defs/Unbundled.lean b/Mathlib/Order/Defs/Unbundled.lean index ae6748e0e4..b45f12284f 100644 --- a/Mathlib/Order/Defs/Unbundled.lean +++ b/Mathlib/Order/Defs/Unbundled.lean @@ -97,6 +97,16 @@ that is, `Std.Trichotomous lt` and `IsStrictOrder X lt`. -/ class IsStrictTotalOrder (α : Sort*) (lt : α → α → Prop) : Prop extends Std.Trichotomous lt, IsStrictOrder α lt +theorem Equivalence.of_isEquiv {α : Sort*} (lt : α → α → Prop) [IsEquiv α lt] : Equivalence lt where + refl := Std.Refl.refl; symm := Std.Symm.symm _ _; trans := IsTrans.trans _ _ _ + +theorem IsEquiv.of_equivalence {α : Sort*} {lt : α → α → Prop} (h : Equivalence lt) : + IsEquiv α lt where + refl := h.refl; symm _ _ := h.symm; trans _ _ _ := h.trans + +theorem equivalence_iff_isEquiv {α : Sort*} (lt : α → α → Prop) : Equivalence lt ↔ IsEquiv α lt := + ⟨.of_equivalence, fun _ => .of_isEquiv lt⟩ + /-- Equality is an equivalence relation. -/ instance eq_isEquiv (α : Sort*) : IsEquiv α (· = ·) where symm := @Eq.symm _ From 6152136b5fcd0124e04ecf9b36de1a63edcaa29d Mon Sep 17 00:00:00 2001 From: Gian Sanjaya <43656481+mortarsanjaya@users.noreply.github.com> Date: Sun, 10 May 2026 10:14:21 +0000 Subject: [PATCH 41/65] feat(Algebra/Order/Ring/Unbundled/Basic): add a generalization of `two_mul_le_add_of_sq_eq_mul` (#38093) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The equality `r^2 = a * b` in the hypothesis can be weakened to `r^2 ≤ a * b`. The implementation requires adding an appropriate `ZeroLEOneClass` instance. --- .../Order/BigOperators/Ring/Finset.lean | 4 ++-- .../Algebra/Order/Ring/Unbundled/Basic.lean | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean index aeb1f3feb8..d027146042 100644 --- a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean +++ b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean @@ -141,8 +141,8 @@ lemma sum_sq_le_sum_mul_sum_of_sq_eq_mul [CommSemiring R] [LinearOrder R] [IsStr gcongr with i hi have ht : (r i * (∑ j ∈ s, g j) * (∑ j ∈ s, r j)) ^ 2 = (f i * (∑ j ∈ s, g j) ^ 2) * (g i * (∑ j ∈ s, r j) ^ 2) := by grind - refine le_of_eq_of_le ?_ (two_mul_le_add_of_sq_eq_mul - (mul_nonneg (hf i hi) (sq_nonneg _)) (mul_nonneg (hg i hi) (sq_nonneg _)) ht) + refine le_of_eq_of_le ?_ (two_mul_le_add_of_sq_le_mul + (mul_nonneg (hf i hi) (sq_nonneg _)) (mul_nonneg (hg i hi) (sq_nonneg _)) ht.le) repeat rw [mul_assoc] _ = _ := by simp_rw [sum_add_distrib, ← sum_mul]; ring diff --git a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean index 2c62bef203..cd50c66319 100644 --- a/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean +++ b/Mathlib/Algebra/Order/Ring/Unbundled/Basic.lean @@ -711,6 +711,10 @@ alias pow_two_nonneg := sq_nonneg lemma mul_self_nonneg [ExistsAddOfLE R] [PosMulMono R] [AddLeftMono R] (a : R) : 0 ≤ a * a := by simpa only [sq] using sq_nonneg a +instance (priority := 100) [ExistsAddOfLE R] [PosMulMono R] [AddLeftMono R] : + ZeroLEOneClass R where + zero_le_one := by simpa only [one_mul] using mul_self_nonneg (1 : R) + /-- The sum of two squares is zero iff both elements are zero. -/ lemma mul_self_add_mul_self_eq_zero [NoZeroDivisors R] [ExistsAddOfLE R] [PosMulMono R] [AddLeftMono R] : @@ -779,13 +783,18 @@ alias four_mul_le_pow_two_add := four_mul_le_sq_add /-- Binary and division-free **arithmetic mean-geometric mean inequality** (aka AM-GM inequality) for linearly ordered commutative semirings. -/ -lemma two_mul_le_add_of_sq_eq_mul [ExistsAddOfLE R] [MulPosStrictMono R] [PosMulStrictMono R] +lemma two_mul_le_add_of_sq_le_mul [ExistsAddOfLE R] [MulPosStrictMono R] [PosMulStrictMono R] [AddLeftReflectLE R] [AddLeftMono R] {a b r : R} - (ha : 0 ≤ a) (hb : 0 ≤ b) (ht : r ^ 2 = a * b) : 2 * r ≤ a + b := by + (ha : 0 ≤ a) (hb : 0 ≤ b) (ht : r ^ 2 ≤ a * b) : 2 * r ≤ a + b := by apply nonneg_le_nonneg_of_sq_le_sq (Left.add_nonneg ha hb) - conv_rhs => rw [← pow_two] - convert four_mul_le_sq_add a b using 1 - rw [mul_mul_mul_comm, two_mul, two_add_two_eq_four, ← pow_two, ht, mul_assoc] + rw [mul_mul_mul_comm, ← pow_two r, two_mul, two_add_two_eq_four] + grw [mul_le_mul_of_nonneg_left ht zero_le_four, ← mul_assoc, four_mul_le_sq_add a b, sq] + +@[deprecated two_mul_le_add_of_sq_le_mul (since := "2026-04-20")] +lemma two_mul_le_add_of_sq_eq_mul [ExistsAddOfLE R] [MulPosStrictMono R] [PosMulStrictMono R] + [AddLeftReflectLE R] [AddLeftMono R] {a b r : R} + (ha : 0 ≤ a) (hb : 0 ≤ b) (ht : r ^ 2 = a * b) : 2 * r ≤ a + b := + two_mul_le_add_of_sq_le_mul ha hb ht.le end LinearOrderedCommSemiring From f764ddfe0a81261a634e61e42c127db7e6421d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 10:14:23 +0000 Subject: [PATCH 42/65] feat(AlgebraicTopology/DoldKan): the homotopy equivalence given by a splitting (#38954) --- .../DoldKan/Degeneracies.lean | 2 +- .../DoldKan/SplitSimplicialObject.lean | 79 ++++++++++++++++++- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/Mathlib/AlgebraicTopology/DoldKan/Degeneracies.lean b/Mathlib/AlgebraicTopology/DoldKan/Degeneracies.lean index da88b2e9ec..5601ba9c94 100644 --- a/Mathlib/AlgebraicTopology/DoldKan/Degeneracies.lean +++ b/Mathlib/AlgebraicTopology/DoldKan/Degeneracies.lean @@ -136,7 +136,7 @@ theorem degeneracy_comp_PInfty (X : SimplicialObject C) (n : ℕ) {Δ' : Simplex fin_cases y rfl · obtain ⟨i, α, h⟩ := SimplexCategory.eq_σ_comp_of_not_injective θ hθ - rw [h, op_comp, X.map_comp, assoc, show X.map (SimplexCategory.σ i).op = X.σ i by rfl, + rw [h, op_comp, X.map_comp, assoc, ← SimplicialObject.σ_def, σ_comp_PInfty, comp_zero] section diff --git a/Mathlib/AlgebraicTopology/DoldKan/SplitSimplicialObject.lean b/Mathlib/AlgebraicTopology/DoldKan/SplitSimplicialObject.lean index df1580324d..fcc989fb6f 100644 --- a/Mathlib/AlgebraicTopology/DoldKan/SplitSimplicialObject.lean +++ b/Mathlib/AlgebraicTopology/DoldKan/SplitSimplicialObject.lean @@ -5,9 +5,9 @@ Authors: Joël Riou -/ module -public import Mathlib.AlgebraicTopology.SimplicialObject.Split public import Mathlib.AlgebraicTopology.DoldKan.Degeneracies -public import Mathlib.AlgebraicTopology.DoldKan.FunctorN +public import Mathlib.AlgebraicTopology.DoldKan.HomotopyEquivalence +public import Mathlib.AlgebraicTopology.SimplicialObject.Split /-! @@ -157,7 +157,9 @@ theorem ιSummand_comp_d_comp_πSummand_eq_zero (j k : ℕ) (A : IndexSet (op set_option backward.isDefEq.respectTransparency false in /-- If `s` is a splitting of a simplicial object `X` in a preadditive category, `s.nondegComplex` is a chain complex which is given in degree `n` by -the nondegenerate `n`-simplices of `X`. -/ +the nondegenerate `n`-simplices of `X`. This chain complex should be thought +as the normalized chain complex of `X` because of the isomorphism +`toKaroubiNondegComplexIsoN₁`. -/ @[simps] noncomputable def nondegComplex : ChainComplex C ℕ where X := s.N @@ -222,6 +224,77 @@ noncomputable def toKaroubiNondegComplexIsoN₁ : simp only [πSummand_comp_cofan_inj_id_comp_PInfty_eq_PInfty, Karoubi.comp_f, HomologicalComplex.comp_f, N₁_obj_p, Karoubi.id_f] +@[reassoc (attr := simp)] +lemma toKaroubiNondegComplexIsoN₁_hom_f_PInfty : + dsimp% s.toKaroubiNondegComplexIsoN₁.hom.f ≫ PInfty = + s.toKaroubiNondegComplexIsoN₁.hom.f := by + simpa using s.toKaroubiNondegComplexIsoN₁.hom.comm + +@[reassoc (attr := simp)] +lemma toKaroubiNondegComplexIsoN₁_hom_inv_id_f : + dsimp% s.toKaroubiNondegComplexIsoN₁.hom.f ≫ s.toKaroubiNondegComplexIsoN₁.inv.f = 𝟙 _ := by + rw [← dsimp% [-Karoubi.comp_f] Karoubi.comp_f s.toKaroubiNondegComplexIsoN₁.hom + s.toKaroubiNondegComplexIsoN₁.inv, Iso.hom_inv_id] + simp + +/-- Given a splitting `s` of a simplicial object `X` in a preadditive category, +this is the split epimorphism from the alternating face map complex of `X` to the chain +complex `s.nondegComplex`. -/ +@[no_expose] +noncomputable def toNondegComplex : K[X] ⟶ s.nondegComplex := + (fullyFaithfulToKaroubi _).preimage + ({ f := by exact PInfty } ≫ s.toKaroubiNondegComplexIsoN₁.inv) + +/-- Given a splitting `s` of a simplicial object `X` in a preadditive category, +this is the split monomormphism from the chain complex `s.nondegComplex` to +the alternating face map complex fo `X`. -/ +@[no_expose] +noncomputable def fromNondegComplex : s.nondegComplex ⟶ K[X] := + (fullyFaithfulToKaroubi _).preimage + (s.toKaroubiNondegComplexIsoN₁.hom ≫ { f := PInfty }) + +@[reassoc (attr := simp)] +lemma PInfty_toNondegComplex : PInfty ≫ s.toNondegComplex = s.toNondegComplex := + (toKaroubi _).map_injective (by simp [toNondegComplex]) + +@[reassoc (attr := simp)] +lemma fromNondegComplex_toNondegComplex : + s.fromNondegComplex ≫ s.toNondegComplex = 𝟙 _ := + (toKaroubi _).map_injective (by simp [toNondegComplex, fromNondegComplex]) + +@[reassoc] +lemma toNondegComplex_f (n : ℕ) : + s.toNondegComplex.f n = PInfty.f n ≫ s.toKaroubiNondegComplexIsoN₁.inv.f.f n := by + simp [toNondegComplex, fullyFaithfulToKaroubi] + +@[reassoc] +lemma fromNondegComplex_f (n : ℕ) : + s.fromNondegComplex.f n = s.ι n ≫ PInfty.f n := by + simp [fromNondegComplex, fullyFaithfulToKaroubi, + cofan, IndexSet.id, IndexSet.e] + +instance isSplitEpi_toNondegComplex : IsSplitEpi s.toNondegComplex where + exists_splitEpi := ⟨⟨s.fromNondegComplex, by simp⟩⟩ + +instance isSplitMono_fromNondegComplex : IsSplitMono s.fromNondegComplex where + exists_splitMono := ⟨⟨s.toNondegComplex, by simp⟩⟩ + +@[reassoc (attr := simp)] +lemma toNondegComplex_fromNondegComplex : + s.toNondegComplex ≫ s.fromNondegComplex = PInfty := + (toKaroubi _).map_injective (by simp [toNondegComplex, fromNondegComplex]) + +/-- Given a splitting `s` of a simplicial object `X` in a preadditive category, +this is the homotopy equivalence from the alternating face map complex of `X` +to the chain complex `s.nondegComplex`. -/ +@[simps hom inv] +noncomputable def homotopyEquivNondegComplex : + HomotopyEquiv K[X] s.nondegComplex where + hom := s.toNondegComplex + inv := s.fromNondegComplex + homotopyHomInvId := .trans (.ofEq (by simp)) (homotopyPInftyToId X) + homotopyInvHomId := .ofEq (by simp) + end Splitting namespace Split From c180dc7716dc75b37155c1d460a0ee7d5bd758ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 10:14:25 +0000 Subject: [PATCH 43/65] feat(CategoryTheory): horizontal composition of Guitart exact squares (#39024) --- Mathlib.lean | 1 + .../CategoryTheory/GuitartExact/Basic.lean | 16 ++ .../GuitartExact/HorizontalComposition.lean | 158 ++++++++++++++++++ .../GuitartExact/VerticalComposition.lean | 26 +++ 4 files changed, 201 insertions(+) create mode 100644 Mathlib/CategoryTheory/GuitartExact/HorizontalComposition.lean diff --git a/Mathlib.lean b/Mathlib.lean index 7a8cfab5a0..9876bcf2d4 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -2720,6 +2720,7 @@ public import Mathlib.CategoryTheory.Groupoid.Grpd.Basic public import Mathlib.CategoryTheory.Groupoid.Subgroupoid public import Mathlib.CategoryTheory.Groupoid.VertexGroup public import Mathlib.CategoryTheory.GuitartExact.Basic +public import Mathlib.CategoryTheory.GuitartExact.HorizontalComposition public import Mathlib.CategoryTheory.GuitartExact.KanExtension public import Mathlib.CategoryTheory.GuitartExact.Opposite public import Mathlib.CategoryTheory.GuitartExact.Over diff --git a/Mathlib/CategoryTheory/GuitartExact/Basic.lean b/Mathlib/CategoryTheory/GuitartExact/Basic.lean index 414b891b5a..641501555b 100644 --- a/Mathlib/CategoryTheory/GuitartExact/Basic.lean +++ b/Mathlib/CategoryTheory/GuitartExact/Basic.lean @@ -241,6 +241,14 @@ instance [hw : w.GuitartExact] {X₂ : C₂} (g : StructuredArrow (R.obj X₂) B rw [guitartExact_iff_isConnected_downwards] at hw apply hw +lemma costructuredArrowRightwards_final_iff_of_iso {X₃ X₃' : C₃} (e : X₃ ≅ X₃') : + (w.costructuredArrowRightwards X₃).Final ↔ + (w.costructuredArrowRightwards X₃').Final := by + rw [Functor.final_iff_comp_equivalence _ (CostructuredArrow.mapIso (B.mapIso e)).functor, + Functor.final_iff_equivalence_comp (CostructuredArrow.mapIso e).functor] + exact Functor.final_natIso_iff + (NatIso.ofComponents (fun _ ↦ CostructuredArrow.isoMk (Iso.refl _))) + lemma guitartExact_iff_final : w.GuitartExact ↔ ∀ (X₃ : C₃), (w.costructuredArrowRightwards X₃).Final := ⟨fun _ _ => ⟨fun _ => inferInstance⟩, fun _ => ⟨fun _ => inferInstance⟩⟩ @@ -250,6 +258,14 @@ instance [hw : w.GuitartExact] (X₃ : C₃) : rw [guitartExact_iff_final] at hw apply hw +lemma structuredArrowDownwards_initial_iff_of_iso {X₂ X₂' : C₂} (e : X₂ ≅ X₂') : + (w.structuredArrowDownwards X₂).Initial ↔ + (w.structuredArrowDownwards X₂').Initial := by + rw [Functor.initial_iff_comp_equivalence _ (StructuredArrow.mapIso (R.mapIso e)).functor, + Functor.initial_iff_equivalence_comp (StructuredArrow.mapIso e).functor] + exact Functor.initial_natIso_iff + (NatIso.ofComponents (fun _ ↦ StructuredArrow.isoMk (Iso.refl _))) + lemma guitartExact_iff_initial : w.GuitartExact ↔ ∀ (X₂ : C₂), (w.structuredArrowDownwards X₂).Initial := ⟨fun _ _ => ⟨fun _ => inferInstance⟩, by diff --git a/Mathlib/CategoryTheory/GuitartExact/HorizontalComposition.lean b/Mathlib/CategoryTheory/GuitartExact/HorizontalComposition.lean new file mode 100644 index 0000000000..7511dae8c6 --- /dev/null +++ b/Mathlib/CategoryTheory/GuitartExact/HorizontalComposition.lean @@ -0,0 +1,158 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.CategoryTheory.GuitartExact.Opposite + +/-! +# Horizontal composition of Guitart exact squares + +In this file, we show that the horizontal composition of Guitart exact squares +is Guitart exact. + +-/ + +@[expose] public section + +namespace CategoryTheory + +open Category + +variable {C₁ C₂ C₃ D₁ D₂ D₃ : Type*} [Category* C₁] [Category* C₂] [Category* C₃] + [Category* D₁] [Category* D₂] [Category* D₃] + +namespace TwoSquare + +section WhiskerHorizontal + +variable {T : C₁ ⥤ D₁} {L : C₁ ⥤ C₂} {R : D₁ ⥤ D₂} {B : C₂ ⥤ D₂} (w : TwoSquare T L R B) + {T' : C₁ ⥤ D₁} {B' : C₂ ⥤ D₂} + +/-- Given `w : TwoSquare T L R B`, one may obtain a 2-square `TwoSquare T' L R B'` if we +provide natural transformations `α : T ⟶ T'` and `β : B' ⟶ B`. -/ +@[simps!] +def whiskerHorizontal (α : T' ⟶ T) (β : B ⟶ B') : + TwoSquare T' L R B' := + (w.whiskerTop α).whiskerBottom β + +namespace GuitartExact + +/-- A 2-square stays Guitart exact if we replace the top and bottom functors +by isomorphic functors. See also `whiskerHorizontal_iff`. -/ +lemma whiskerHorizontal [w.GuitartExact] (α : T ≅ T') (β : B ≅ B') : + (w.whiskerHorizontal α.inv β.hom).GuitartExact := by + rw [guitartExact_iff_final] + intro X₂ + let e : costructuredArrowRightwards (w.whiskerHorizontal α.inv β.hom) X₂ ≅ + w.costructuredArrowRightwards X₂ ⋙ (CostructuredArrow.mapIso (β.app X₂)).functor := + NatIso.ofComponents (fun f ↦ CostructuredArrow.isoMk (α.symm.app f.left)) + rw [Functor.final_natIso_iff e] + infer_instance + +/-- A 2-square is Guitart exact iff it is so after replacing the top and bottom functors by +isomorphic functors. -/ +@[simp] +lemma whiskerHorizontal_iff (α : T ≅ T') (β : B ≅ B') : + (w.whiskerHorizontal α.inv β.hom).GuitartExact ↔ w.GuitartExact := by + rw [← guitartExact_op_iff, ← w.guitartExact_op_iff, + ← whiskerVertical_iff w.op (NatIso.op α.symm) (NatIso.op β.symm)] + rfl + +instance [w.GuitartExact] (α : T' ⟶ T) (β : B ⟶ B') + [IsIso α] [IsIso β] : (w.whiskerHorizontal α β).GuitartExact := + whiskerHorizontal w (asIso α).symm (asIso β) + +end GuitartExact + +end WhiskerHorizontal + +section HorizontalComposition + +variable {V₁ : C₁ ⥤ D₁} {T₁ : C₁ ⥤ C₂} {B₁ : D₁ ⥤ D₂} {V₂ : C₂ ⥤ D₂} + (w : TwoSquare T₁ V₁ V₂ B₁) + {T₂ : C₂ ⥤ C₃} {B₂ : D₂ ⥤ D₃} {V₃ : C₃ ⥤ D₃} + (w' : TwoSquare T₂ V₂ V₃ B₂) + +/-- The horizontal composition of 2-squares. (Variant where we allow the replacement of +the horizontal compositions by isomorphic functors.) -/ +@[simps!] +def hComp' {T₁₂ : C₁ ⥤ C₃} {B₁₂ : D₁ ⥤ D₃} (eT : T₁ ⋙ T₂ ≅ T₁₂) (eB : B₁ ⋙ B₂ ≅ B₁₂) : + TwoSquare T₁₂ V₁ V₃ B₁₂ := + (w ≫ₕ w').whiskerHorizontal eT.inv eB.hom + +namespace GuitartExact + +instance hComp [w.GuitartExact] [w'.GuitartExact] : + (w ≫ₕ w').GuitartExact := by + rw [← guitartExact_op_iff] + have : (w ≫ₕ w').op = w.op ≫ᵥ w'.op := by ext; simp + rw [this] + exact inferInstanceAs (w.op ≫ᵥ w'.op).GuitartExact + +instance hComp' {T₁₂ : C₁ ⥤ C₃} {B₁₂ : D₁ ⥤ D₃} (eT : T₁ ⋙ T₂ ≅ T₁₂) (eB : B₁ ⋙ B₂ ≅ B₁₂) + [w.GuitartExact] [w'.GuitartExact] : + (w.hComp' w' eT eB).GuitartExact := by + dsimp only [TwoSquare.hComp'] + infer_instance + +/-- The canonical isomorphism between +`w.costructuredArrowRightwards Y₁ ⋙ w'.costructuredArrowRightwards (B₁.obj Y₁)` and +`(w ≫ₕ w').costructuredArrowRightwards Y₁`. -/ +def costructuredArrowRightwardsComp (Y₁ : D₁) : + w.costructuredArrowRightwards Y₁ ⋙ w'.costructuredArrowRightwards (B₁.obj Y₁) ≅ + (w ≫ₕ w').costructuredArrowRightwards Y₁ := + NatIso.ofComponents (fun _ => CostructuredArrow.isoMk (Iso.refl _)) + +lemma of_hComp [B₁.EssSurj] [w.GuitartExact] [(w ≫ₕ w').GuitartExact] : + w'.GuitartExact := by + rw [guitartExact_iff_final] + intro Y₂ + rw [costructuredArrowRightwards_final_iff_of_iso _ (B₁.objObjPreimageIso Y₂).symm] + have : (w.costructuredArrowRightwards (B₁.objPreimage Y₂) ⋙ + w'.costructuredArrowRightwards (B₁.obj (B₁.objPreimage Y₂))).Final := + (Functor.final_of_natIso (costructuredArrowRightwardsComp w w' _).symm :) + exact Functor.final_of_final_comp (w.costructuredArrowRightwards (B₁.objPreimage Y₂)) _ + +lemma of_hComp' {T₁₂ : C₁ ⥤ C₃} {B₁₂ : D₁ ⥤ D₃} (eT : T₁ ⋙ T₂ ≅ T₁₂) (eB : B₁ ⋙ B₂ ≅ B₁₂) + [B₁.EssSurj] [w.GuitartExact] [h : (w.hComp' w' eT eB).GuitartExact] : + w'.GuitartExact := by + dsimp [TwoSquare.hComp'] at h + rw [whiskerHorizontal_iff] at h + exact of_hComp w w' + +lemma hComp_iff_of_essSurj [B₁.EssSurj] [w.GuitartExact] : + (w ≫ₕ w').GuitartExact ↔ w'.GuitartExact := + ⟨fun _ ↦ of_hComp w w', fun _ ↦ inferInstance⟩ + +lemma hComp'_iff_of_essSurj + {T₁₂ : C₁ ⥤ C₃} {B₁₂ : D₁ ⥤ D₃} (eT : T₁ ⋙ T₂ ≅ T₁₂) (eB : B₁ ⋙ B₂ ≅ B₁₂) + [B₁.EssSurj] [w.GuitartExact] : + (w.hComp' w' eT eB).GuitartExact ↔ w'.GuitartExact := + ⟨fun _ ↦ of_hComp' w w' eT eB, fun _ ↦ inferInstance⟩ + +lemma hComp_iff_of_equivalences (eT : C₂ ≌ C₃) (eB : D₂ ≌ D₃) + (w' : eT.functor ⋙ V₃ ≅ V₂ ⋙ eB.functor) : + (w ≫ₕ w'.hom).GuitartExact ↔ w.GuitartExact := by + let w'' : V₂.op ⋙ eB.op.functor ≅ eT.op.functor ⋙ V₃.op := NatIso.op w' + have : (w ≫ₕ w'.hom).op = (w.op ≫ᵥ w''.hom) := by ext; simp [w''] + rw [← guitartExact_op_iff, ← guitartExact_op_iff w, + ← vComp_iff_of_equivalences _ _ _ w'', this] + rfl + +lemma hComp'_iff_of_equivalences (E : C₂ ≌ C₃) (E' : D₂ ≌ D₃) + (w' : E.functor ⋙ V₃ ≅ V₂ ⋙ E'.functor) + {T₁₂ : C₁ ⥤ C₃} {B₁₂ : D₁ ⥤ D₃} (eT : T₁ ⋙ E.functor ≅ T₁₂) + (eB : B₁ ⋙ E'.functor ≅ B₁₂) : + (w.hComp' w'.hom eT eB).GuitartExact ↔ w.GuitartExact := by + rw [← hComp_iff_of_equivalences w E E' w', TwoSquare.hComp', whiskerHorizontal_iff] + +end GuitartExact + +end HorizontalComposition + +end TwoSquare + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/GuitartExact/VerticalComposition.lean b/Mathlib/CategoryTheory/GuitartExact/VerticalComposition.lean index 0bee9f0a31..ca29743e2e 100644 --- a/Mathlib/CategoryTheory/GuitartExact/VerticalComposition.lean +++ b/Mathlib/CategoryTheory/GuitartExact/VerticalComposition.lean @@ -118,6 +118,32 @@ instance vComp' [GuitartExact w] [GuitartExact w'] {L₁₂ : C₁ ⥤ C₃} dsimp only [TwoSquare.vComp'] infer_instance +set_option backward.isDefEq.respectTransparency false in +lemma of_vComp [R₁.EssSurj] [w.GuitartExact] [(w ≫ᵥ w').GuitartExact] : + w'.GuitartExact := by + rw [guitartExact_iff_initial] + intro Y₂ + rw [structuredArrowDownwards_initial_iff_of_iso _ (R₁.objObjPreimageIso Y₂).symm] + have := Functor.initial_of_natIso (structuredArrowDownwardsComp w w' (R₁.objPreimage Y₂)).symm + exact Functor.initial_of_initial_comp (w.structuredArrowDownwards (R₁.objPreimage Y₂)) _ + +lemma of_vComp' {L₁₂ : C₁ ⥤ C₃} {R₁₂ : D₁ ⥤ D₃} (eL : L₁ ⋙ L₂ ≅ L₁₂) (eR : R₁ ⋙ R₂ ≅ R₁₂) + [R₁.EssSurj] [w.GuitartExact] [h : (w.vComp' w' eL eR).GuitartExact] : + w'.GuitartExact := by + dsimp [TwoSquare.vComp'] at h + rw [whiskerVertical_iff] at h + exact of_vComp w w' + +lemma vComp_iff_of_essSurj [R₁.EssSurj] [w.GuitartExact] : + (w ≫ᵥ w').GuitartExact ↔ w'.GuitartExact := + ⟨fun _ ↦ of_vComp w w', fun _ ↦ inferInstance⟩ + +lemma vComp'_iff_of_essSurj + {L₁₂ : C₁ ⥤ C₃} {R₁₂ : D₁ ⥤ D₃} (eL : L₁ ⋙ L₂ ≅ L₁₂) (eR : R₁ ⋙ R₂ ≅ R₁₂) + [R₁.EssSurj] [w.GuitartExact] : + (w.vComp' w' eL eR).GuitartExact ↔ w'.GuitartExact := + ⟨fun _ ↦ of_vComp' w w' eL eR, fun _ ↦ inferInstance⟩ + set_option backward.isDefEq.respectTransparency false in lemma vComp_iff_of_equivalences (eL : C₂ ≌ C₃) (eR : D₂ ≌ D₃) (w' : H₂ ⋙ eR.functor ≅ eL.functor ⋙ H₃) : From 73da2cac0acda4e36dd9a0ed924020672bea3981 Mon Sep 17 00:00:00 2001 From: Christian Merten <136261474+chrisflav@users.noreply.github.com> Date: Sun, 10 May 2026 10:59:45 +0000 Subject: [PATCH 44/65] feat(RingTheory): local isomorphisms (#38176) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We add the class of algebras that are locally (on the geometric source) standard open immersions. This will be used to define ind-Zariski algebras, which are an important tool to study ind-étale algebras. From Proetale. --- Mathlib.lean | 1 + Mathlib/RingTheory/LocalIso.lean | 183 ++++++++++++++++++ Mathlib/RingTheory/RingHom/OpenImmersion.lean | 36 +++- 3 files changed, 217 insertions(+), 3 deletions(-) create mode 100644 Mathlib/RingTheory/LocalIso.lean diff --git a/Mathlib.lean b/Mathlib.lean index 9876bcf2d4..74c3baa467 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -6573,6 +6573,7 @@ public import Mathlib.RingTheory.LaurentSeries public import Mathlib.RingTheory.Length public import Mathlib.RingTheory.LinearDisjoint public import Mathlib.RingTheory.LittleWedderburn +public import Mathlib.RingTheory.LocalIso public import Mathlib.RingTheory.LocalProperties.Basic public import Mathlib.RingTheory.LocalProperties.Exactness public import Mathlib.RingTheory.LocalProperties.Injective diff --git a/Mathlib/RingTheory/LocalIso.lean b/Mathlib/RingTheory/LocalIso.lean new file mode 100644 index 0000000000..ac833ffa20 --- /dev/null +++ b/Mathlib/RingTheory/LocalIso.lean @@ -0,0 +1,183 @@ +/- +Copyright (c) 2026 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jiedong Jiang, Christian Merten +-/ +module + +public import Mathlib.RingTheory.RingHom.OpenImmersion +public import Mathlib.RingTheory.Spectrum.Prime.Topology + +/-! +# Local isomorphisms + +A ring homomorphism is a local isomorphism if source locally (in the geometric sense) +it is a standard open immersion. + +## Main declarations + +- `Algebra.IsLocalIso`: The class of algebras that are locally standard open immersions. + +We show that local isomorphisms are local, stable under composition and base change. + +## Implementation note + +Most results in this file follow purely formally from the corresponding property of +standard open immersion. We could use the `RingHom.Locally` API to obtain them, but +it would yield results with less universe generality and we would have to replace +`CommSemiring` by `CommRing`. In the future, we may consider refactoring the API +for `RingHom` properties to allow for also treating properties of `CommSemiring`s +and then simplify the proofs in this file. +-/ + +universe w v u + +public section + +open TensorProduct + +/-- An `R`-algebra `S` is a local isomorphism if source locally (in the geometric sense), +it is a standard open immersion. -/ +@[stacks 096E "(1) in the algebra formulation", mk_iff] +class Algebra.IsLocalIso (R S : Type*) [CommSemiring R] [CommSemiring S] [Algebra R S] : Prop where + exists_notMem_isStandardOpenImmersion (q : Ideal S) [q.IsPrime] : + ∃ g ∉ q, IsStandardOpenImmersion R (Localization.Away g) + +namespace Algebra.IsLocalIso + +variable {R S : Type*} [CommSemiring R] [CommSemiring S] [Algebra R S] + +variable (R S) in +lemma span_isStandardOpenImmersion_eq_top [Algebra.IsLocalIso R S] : + Ideal.span {g : S | Algebra.IsStandardOpenImmersion R (Localization.Away g)} = ⊤ := by + by_contra hne + obtain ⟨m, hm, hms⟩ := Ideal.exists_le_maximal _ hne + obtain ⟨g, hgm, hstd⟩ := + Algebra.IsLocalIso.exists_notMem_isStandardOpenImmersion (R := R) m + exact hgm (hms (Ideal.subset_span hstd)) + +lemma iff_span_isStandardOpenImmersion_eq_top : + IsLocalIso R S ↔ + Ideal.span {g : S | IsStandardOpenImmersion R (Localization.Away g)} = ⊤ := by + refine ⟨fun _ ↦ span_isStandardOpenImmersion_eq_top R S, fun h ↦ ⟨fun q hq ↦ ?_⟩⟩ + grind [Ideal.IsPrime.ne_top, Ideal.span_le, SetLike.mem_coe] + +instance (priority := 100) [IsStandardOpenImmersion R S] : IsLocalIso R S where + exists_notMem_isStandardOpenImmersion q hq := by + use 1, hq.one_notMem + exact IsStandardOpenImmersion.trans _ S _ + +lemma of_span_range_eq_top {ι : Type*} (f : ι → S) (h : Ideal.span (Set.range f) = ⊤) + (T : ι → Type*) [∀ i, CommSemiring (T i)] [∀ i, Algebra R (T i)] [∀ i, Algebra S (T i)] + [∀ i, IsScalarTower R S (T i)] [∀ i, IsLocalization.Away (f i) (T i)] + [∀ i, IsLocalIso R (T i)] : IsLocalIso R S := by + refine ⟨fun q hq ↦ ?_⟩ + obtain ⟨i, hi⟩ : ∃ i, f i ∉ q := by + rw [← PrimeSpectrum.iSup_basicOpen_eq_top_iff] at h + have : ⟨q, hq⟩ ∈ ⨆ i, PrimeSpectrum.basicOpen (f i) := by simp [h] + simpa using this + have : ⟨q, hq⟩ ∈ PrimeSpectrum.basicOpen (f i) := hi + rw [← SetLike.mem_coe, ← PrimeSpectrum.localization_away_comap_range (T i)] at this + obtain ⟨q', hq'⟩ := this + obtain ⟨g', hg', h⟩ := exists_notMem_isStandardOpenImmersion (R := R) q'.1 + obtain ⟨n, g, hg⟩ := IsLocalization.Away.surj (f i) g' + refine ⟨g * (f i), ?_, ?_⟩ + · refine Ideal.IsPrime.mul_notMem hq ?_ hi + simp only [PrimeSpectrum.ext_iff, PrimeSpectrum.comap_asIdeal] at hq' + rwa [← hq', Ideal.mem_comap, ← hg, Ideal.mul_unit_mem_iff_mem] + exact (IsLocalization.Away.algebraMap_isUnit (f i)).pow n + · have : IsLocalization.Away g' (Localization.Away (algebraMap S (T i) g)) := by + rw [← hg] + exact .of_associated + (associated_mul_unit_right _ _ ((IsLocalization.Away.algebraMap_isUnit (f i)).pow n)).symm + let e₁ : Localization.Away (algebraMap S (T i) g) ≃ₐ[S] Localization.Away (g * f i) := + IsLocalization.algEquiv (.powers (g * f i)) _ _ + let e₂ : Localization.Away g' ≃ₐ[R] Localization.Away (g * f i) := + ((IsLocalization.algEquiv (.powers g') _ _).restrictScalars R).trans (e₁.restrictScalars R) + exact .of_algEquiv e₂ + +lemma of_span_eq_top {s : Set S} (h : Ideal.span s = ⊤) + (h : ∀ x ∈ s, IsLocalIso R (Localization.Away x)) : IsLocalIso R S := by + have heq : Ideal.span (Set.range fun i : s ↦ i.1) = ⊤ := by simpa + have (i : s) : IsLocalIso R (Localization.Away i.1) := h _ i.property + exact .of_span_range_eq_top _ heq fun i ↦ Localization.Away i.1 + +lemma pi_of_finite {ι : Type*} (R : Type*) (S : ι → Type*) [CommSemiring R] + [∀ i, CommRing (S i)] [∀ i, Algebra R (S i)] [Finite ι] [∀ i, IsLocalIso R (S i)] : + IsLocalIso R (∀ i, S i) := by + classical + let (i : ι) : Algebra (∀ i, S i) (S i) := (Pi.evalAlgHom R S i).toAlgebra + have (i : ι) : IsLocalization.Away (Pi.single i (1 : S i)) (S i) := by + apply IsLocalization.away_of_isIdempotentElem + · simp [IsIdempotentElem, ← Pi.single_mul_left] + · apply RingHom.ker_evalRingHom + · apply (Pi.evalRingHom S i).surjective + apply of_span_range_eq_top (fun i ↦ Pi.single i (1 : S i)) _ fun i ↦ S i + exact Ideal.span_single_eq_top _ + +variable (T : Type*) [CommSemiring T] + +attribute [local instance] isScalarTower_localizationAlgebra in +variable (R S) in +/-- Local isomorphisms are stable under composition. -/ +lemma trans [Algebra S T] [Algebra R T] [IsScalarTower R S T] + [IsLocalIso R S] [IsLocalIso S T] : IsLocalIso R T := by + -- The proof is purely formal given that open immersions are stable under composition. + let s : Set S := {g : S | IsStandardOpenImmersion R (Localization.Away g)} + let T' (g : S) := Localization.Away (algebraMap S T g) + let (g : S) : Algebra (Localization.Away g) (T' g) := localizationAlgebra (.powers g) T + let T'' (g : S) (x : T) := Localization.Away (algebraMap _ (T' g) x) + let t (g : S) : Set T := {x : T | IsStandardOpenImmersion (Localization.Away g) (T'' g x)} + let ι : Type _ := Σ i : s, t i.1 + have (i : ι) : IsStandardOpenImmersion (Localization.Away i.1.1) (T'' i.1 i.2) := i.2.2 + suffices h : Ideal.span (Set.range fun i : ι ↦ algebraMap S T i.1 * i.2) = ⊤ by + have (i : ι) : IsStandardOpenImmersion R (T'' i.1 i.2) := + have : IsScalarTower R (Localization.Away i.1.1) (T' i.1.1) := + IsScalarTower.to₁₃₄ _ S _ _ + have : IsStandardOpenImmersion (Localization.Away i.1.1) (T'' i.1.1 i.2.1) := i.2.2 + have : IsStandardOpenImmersion R (Localization.Away i.1.1) := i.1.2 + .trans _ (Localization.Away i.1.1) _ + exact .of_span_range_eq_top _ h fun i : ι ↦ T'' i.1 i.2 + have h1 := congr(Ideal.map (algebraMap S T) $(span_isStandardOpenImmersion_eq_top R S)) + rw [Ideal.map_top, Ideal.map_span] at h1 + nth_rw 1 [_root_.eq_top_iff, ← Ideal.top_mul ⊤, ← h1, ← span_isStandardOpenImmersion_eq_top S T, + Ideal.span_mul_span, Ideal.span_le, Set.mul_subset_iff] + simp only [Set.mem_image, Set.mem_setOf_eq, SetLike.mem_coe, forall_exists_index, and_imp, + forall_apply_eq_imp_iff₂] + intro g hg x hx + refine Ideal.subset_span ⟨⟨⟨g, hg⟩, ⟨x, ?_⟩⟩, rfl⟩ + simp only [Set.mem_setOf_eq, t] + let : Algebra (Localization.Away x) (T'' g x) := + localizationAlgebra (.powers x) (T' g) + have : IsScalarTower S (Localization.Away x) (T'' g x) := + IsScalarTower.to₁₃₄ _ T _ _ + have : IsLocalization (algebraMapSubmonoid (Localization.Away x) (.powers g)) (T'' g x) := by + have : algebraMapSubmonoid (Localization.Away x) (.powers g) = + algebraMapSubmonoid (Localization.Away x) (.powers (algebraMap S T g)) := by + simp [IsScalarTower.algebraMap_apply S T (Localization.Away x)] + rw [this] + exact .commutes _ (T' g) _ (.powers x) (.powers (algebraMap S T g)) + have : IsPushout S (Localization.Away x) (Localization.Away g) (T'' g x) := + Algebra.isPushout_of_isLocalization (.powers g) _ _ _ + exact .of_isPushout S (Localization.Away x) _ _ + +variable {T} in +lemma of_algEquiv [Algebra R T] (e : S ≃ₐ[R] T) [IsLocalIso R S] : IsLocalIso R T := by + algebraize [e.toAlgHom.toRingHom] + have : IsStandardOpenImmersion S T := .of_bijective e.bijective + exact .trans _ S _ + +variable {T} in +lemma iff_of_algEquiv [Algebra R T] (e : S ≃ₐ[R] T) : IsLocalIso R S ↔ IsLocalIso R T := + ⟨fun _ ↦ .of_algEquiv e, fun _ ↦ .of_algEquiv e.symm⟩ + +instance [Algebra R T] [IsLocalIso R S] : IsLocalIso T (T ⊗[R] S) := by + rw [iff_span_isStandardOpenImmersion_eq_top, _root_.eq_top_iff, + ← Ideal.map_top Algebra.TensorProduct.includeRight, ← span_isStandardOpenImmersion_eq_top R S, + Ideal.map_le_iff_le_comap, Ideal.span_le] + intro g hg + apply Ideal.subset_span + simp only [Set.mem_setOf_eq] at hg ⊢ + exact .of_algEquiv <| IsLocalization.Away.tensorProductEquivTMulRight R T g (Localization.Away g) + +end Algebra.IsLocalIso diff --git a/Mathlib/RingTheory/RingHom/OpenImmersion.lean b/Mathlib/RingTheory/RingHom/OpenImmersion.lean index 45d7eb59fd..0d8177217f 100644 --- a/Mathlib/RingTheory/RingHom/OpenImmersion.lean +++ b/Mathlib/RingTheory/RingHom/OpenImmersion.lean @@ -30,13 +30,13 @@ variable {R S T : Type*} [CommSemiring R] [CommSemiring S] [CommSemiring T] [Algebra R S] : Prop where exists_away (R S) : ∃ r : R, IsLocalization.Away r S -open IsStandardOpenImmersion +namespace IsStandardOpenImmersion instance (r : R) : IsStandardOpenImmersion R (Localization.Away r) := ⟨r, inferInstance⟩ variable (R S T) in -@[trans] theorem IsStandardOpenImmersion.trans [Algebra S T] [IsScalarTower R S T] +@[trans] theorem trans [Algebra S T] [IsScalarTower R S T] [IsStandardOpenImmersion R S] [IsStandardOpenImmersion S T] : IsStandardOpenImmersion R T := let ⟨r, _⟩ := exists_away R S @@ -50,7 +50,37 @@ instance [IsStandardOpenImmersion R T] : IsStandardOpenImmersion S (S ⊗[R] T) let ⟨r, _⟩ := exists_away R T ⟨algebraMap R S r, inferInstance⟩ -end Algebra +instance : IsStandardOpenImmersion R R := + ⟨1, IsLocalization.away_of_isUnit_of_bijective R isUnit_one Function.bijective_id⟩ + +lemma of_bijective (h : Function.Bijective (algebraMap R S)) : + IsStandardOpenImmersion R S := by + rw [Algebra.isStandardOpenImmersion_iff] + use 1 + apply IsLocalization.away_of_isUnit_of_bijective _ isUnit_one h + +lemma of_algEquiv {T : Type*} [CommSemiring T] [Algebra R T] (e : S ≃ₐ[R] T) + [h : IsStandardOpenImmersion R S] : + IsStandardOpenImmersion R T := by + rw [Algebra.isStandardOpenImmersion_iff] at * + obtain ⟨r, hr⟩ := h + use r + exact IsLocalization.isLocalization_of_algEquiv _ e + +lemma iff_of_algEquiv {T : Type*} [CommSemiring T] [Algebra R T] + (e : S ≃ₐ[R] T) : + IsStandardOpenImmersion R S ↔ IsStandardOpenImmersion R T := + ⟨fun _ ↦ .of_algEquiv e, fun _ ↦ .of_algEquiv e.symm⟩ + +variable (R S) in +lemma of_isPushout (R' S' : Type*) [CommSemiring R'] [CommSemiring S'] + [Algebra R R'] [Algebra S S'] [Algebra R' S'] [Algebra R S'] [IsScalarTower R R' S'] + [IsScalarTower R S S'] [IsPushout R S R' S'] [IsStandardOpenImmersion R S] : + IsStandardOpenImmersion R' S' := + have : IsPushout R R' S S' := by rwa [IsPushout.comm] + .of_algEquiv (IsPushout.equiv R _ S _) + +end Algebra.IsStandardOpenImmersion namespace RingHom From 99178e15ddd10b0702a43994c0662a4301b06f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 10:59:47 +0000 Subject: [PATCH 45/65] feat(AlgebraicTopology/SimplicialSet): faces of the boundary (#38662) --- .../SimplicialSet/Boundary.lean | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/Boundary.lean b/Mathlib/AlgebraicTopology/SimplicialSet/Boundary.lean index 3233f05c3d..4c091d92f1 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/Boundary.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/Boundary.lean @@ -88,6 +88,14 @@ lemma boundary_obj_eq_univ (m n : ℕ) (h : m < n := by lia) : ← Subcomplex.ofSimplex_le_iff] apply Subcomplex.ofSimplex_map_le +@[simp] +lemma boundary_zero : boundary.{u} 0 = ⊥ := by + ext m x + simp only [boundary, Nat.reduceAdd, Set.mem_setOf_eq, Subfunctor.bot_obj, Set.bot_eq_empty, + Set.mem_empty_iff_false, iff_false, Decidable.not_not] + intro x + exact ⟨0, by subsingleton⟩ + lemma op_boundary (n : ℕ) : ∂Δ[n].op.preimage (stdSimplex.opIso.{u} ⦋n⦌).inv = ∂Δ[n] := by ext ⟨⟨d⟩⟩ j @@ -141,4 +149,63 @@ lemma eq_boundary_iff : end stdSimplex +namespace boundary + +/-- The inclusion of a face of `∂Δ[n]`. -/ +def faceι {n : ℕ} (i : Fin (n + 1)) : + (stdSimplex.face {i}ᶜ : SSet.{u}) ⟶ ∂Δ[n] := + Subcomplex.homOfLE (face_singleton_compl_le_boundary i) + +instance {n : ℕ} (i : Fin (n + 1)) : Mono (faceι.{u} i) := by + dsimp [faceι]; infer_instance + +@[reassoc (attr := simp)] +lemma faceι_ι {n : ℕ} (i : Fin (n + 2)) : + faceι i ≫ (boundary.{u} (n + 1)).ι = (stdSimplex.face {i}ᶜ).ι := by + simp [faceι] + +/-- The morphism `Δ[n] ⟶ ∂Δ[n + 1]` corresponding to face +the face `i : Fin (n + 2)`. -/ +def ι {n : ℕ} (i : Fin (n + 2)) : + Δ[n] ⟶ (∂Δ[n + 1] : SSet.{u}) := + Subcomplex.lift ((stdSimplex.{u}.map (SimplexCategory.δ i))) (by + simp only [Subcomplex.range_eq_ofSimplex] + refine le_trans ?_ (face_singleton_compl_le_boundary i) + rw [stdSimplex.face_singleton_compl, yonedaEquiv_map]) + +@[reassoc (attr := simp)] +lemma ι_ι {n : ℕ} (i : Fin (n + 2)) : + ι.{u} i ≫ ∂Δ[n + 1].ι = stdSimplex.δ i := rfl + +@[reassoc (attr := simp)] +lemma faceSingletonComplIso_inv_ι {n : ℕ} (i : Fin (n + 2)) : + (stdSimplex.faceSingletonComplIso i).inv ≫ ι i = boundary.faceι i := by + rw [← cancel_epi (stdSimplex.faceSingletonComplIso i).hom, Iso.hom_inv_id_assoc] + rfl + +instance {n : ℕ} (i : Fin (n + 2)) : Mono (ι.{u} i) := by + rw [← mono_comp_iff_of_isIso (stdSimplex.faceSingletonComplIso i).inv, + faceSingletonComplIso_inv_ι] + infer_instance + +instance {n : ℕ} (i : Fin (n + 2)) : Mono (stdSimplex.{u}.δ i) := by + rw [← ι_ι] + infer_instance + +lemma hom_ext {n : ℕ} {X : SSet.{u}} {f g : (∂Δ[n + 1] : SSet) ⟶ X} + (h : ∀ (i : Fin (n + 2)), ι i ≫ f = ι i ≫ g) : + f = g := by + ext m ⟨x, hx⟩ + simp only [boundary_eq_iSup, stdSimplex.face_singleton_compl, Subfunctor.iSup_obj, + Set.mem_iUnion, Subcomplex.mem_ofSimplex_obj_iff, op_unop] at hx + obtain ⟨i, ⟨y, rfl⟩⟩ := hx + exact ConcreteCategory.congr_hom (congr_app (h i) _) _ + +@[ext] +lemma hom_ext₀ {X : SSet.{u}} {f g : (∂Δ[0] : SSet) ⟶ X} : f = g := by + ext _ ⟨x, hx⟩ + simp at hx + +end boundary + end SSet From fcf5297732126d706487d1b1aa2197f4b340efc4 Mon Sep 17 00:00:00 2001 From: Thomas Browning <13339017+tb65536@users.noreply.github.com> Date: Sun, 10 May 2026 10:59:49 +0000 Subject: [PATCH 46/65] chore(GroupTheory/SpecificGroups/Alternating): deprecate old proof of simplicity of A_5 (#38670) Now that #36524 has been merged, we can deprecate the old proof of simplicity of A_5. Co-authored-by: tb65536 --- Mathlib/Algebra/Group/Subgroup/Basic.lean | 4 + Mathlib/GroupTheory/Perm/Cycle/Type.lean | 4 + .../SpecificGroups/Alternating.lean | 107 +----------------- .../SpecificGroups/Alternating/Simple.lean | 28 ++++- 4 files changed, 37 insertions(+), 106 deletions(-) diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean index 533e1dc3b4..83b4cc4a47 100644 --- a/Mathlib/Algebra/Group/Subgroup/Basic.lean +++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean @@ -559,6 +559,10 @@ theorem normalClosure_le_normal {N : Subgroup G} [N.Normal] (h : s ⊆ N) : norm theorem normalClosure_subset_iff {N : Subgroup G} [N.Normal] : s ⊆ N ↔ normalClosure s ≤ N := ⟨normalClosure_le_normal, Set.Subset.trans subset_normalClosure⟩ +@[simp] +theorem normalClosure_eq_bot_iff : normalClosure s = ⊥ ↔ s ⊆ {1} := by + rw [eq_bot_iff, ← normalClosure_subset_iff, coe_bot] + @[to_additive (attr := gcongr)] theorem normalClosure_mono {s t : Set G} (h : s ⊆ t) : normalClosure s ≤ normalClosure t := normalClosure_le_normal (Set.Subset.trans h subset_normalClosure) diff --git a/Mathlib/GroupTheory/Perm/Cycle/Type.lean b/Mathlib/GroupTheory/Perm/Cycle/Type.lean index aeed8b9e53..353e548c29 100644 --- a/Mathlib/GroupTheory/Perm/Cycle/Type.lean +++ b/Mathlib/GroupTheory/Perm/Cycle/Type.lean @@ -627,6 +627,10 @@ variable [DecidableEq α] {σ : Perm α} theorem cycleType (h : IsThreeCycle σ) : σ.cycleType = {3} := h +theorem ne_one (h : IsThreeCycle σ) : σ ≠ 1 := by + rintro rfl + simpa using h.cycleType + theorem card_support (h : IsThreeCycle σ) : #σ.support = 3 := by rw [← sum_cycleType, h.cycleType, Multiset.sum_singleton] diff --git a/Mathlib/GroupTheory/SpecificGroups/Alternating.lean b/Mathlib/GroupTheory/SpecificGroups/Alternating.lean index d062750359..c0119c006c 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Alternating.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Alternating.lean @@ -273,19 +273,6 @@ theorem _root_.alternatingGroup.closure_cycleType_eq_two_two_eq_top (h5 : 5 ≤ have := closure_cycleType_eq_two_two_eq_alternatingGroup h5 aesop -/-- A key lemma to prove $A_5$ is simple. Shows that any normal subgroup of an alternating group on - at least 5 elements is the entire alternating group if it contains a 3-cycle. -/ -theorem IsThreeCycle.alternating_normalClosure (h5 : 5 ≤ Nat.card α) {f : Perm α} - (hf : IsThreeCycle f) : - normalClosure ({⟨f, hf.mem_alternatingGroup⟩} : Set (alternatingGroup α)) = ⊤ := by - rw [eq_top_iff, ← map_subtype_le_map_subtype, ← MonoidHom.range_eq_map, range_subtype, - normalClosure, MonoidHom.map_closure] - refine (le_of_eq closure_three_cycles_eq_alternating.symm).trans (closure_mono fun g h ↦ ?_) - obtain ⟨c, rfl⟩ := isConj_iff.mp (isConj_iff_cycleType_eq.mpr (hf.trans h.symm)) - refine ⟨⟨c * f * c⁻¹, h.mem_alternatingGroup⟩, ?_, rfl⟩ - rw [Group.mem_conjugatesOfSet_iff] - exact ⟨⟨f, hf.mem_alternatingGroup⟩, Set.mem_singleton _, isThreeCycle_isConj h5 hf h⟩ - /-- Part of proving $A_5$ is simple. Shows that the square of any element of $A_5$ with a 3-cycle in its cycle decomposition is a 3-cycle, so the normal closure of the original element must be $A_5$. -/ @@ -330,52 +317,12 @@ theorem nontrivial_of_three_le_card (h3 : 3 ≤ Nat.card α) : Nontrivial (alter instance {n : ℕ} : Nontrivial (alternatingGroup (Fin (n + 3))) := nontrivial_of_three_le_card (by simp) -/-- The normal closure of the 5-cycle `finRotate 5` within $A_5$ is the whole group. This will be - used to show that the normal closure of any 5-cycle within $A_5$ is the whole group. -/ -theorem normalClosure_finRotate_five : normalClosure ({⟨finRotate 5, - finRotate_bit1_mem_alternatingGroup (n := 2)⟩} : Set (alternatingGroup (Fin 5))) = ⊤ := - eq_top_iff.2 - (by - have h3 : - IsThreeCycle (Fin.cycleRange 2 * finRotate 5 * (Fin.cycleRange 2)⁻¹ * (finRotate 5)⁻¹) := - card_support_eq_three_iff.1 (by decide) - rw [← h3.alternating_normalClosure (by rw [Nat.card_fin])] - refine normalClosure_le_normal ?_ - rw [Set.singleton_subset_iff, SetLike.mem_coe] - have h : - (⟨finRotate 5, finRotate_bit1_mem_alternatingGroup (n := 2)⟩ : alternatingGroup (Fin 5)) ∈ - normalClosure _ := - SetLike.mem_coe.1 (subset_normalClosure (Set.mem_singleton _)) - -- Porting note: added `:` to help the elaborator (otherwise we get a timeout) - exact (mul_mem (Subgroup.normalClosure_normal.conj_mem _ h - ⟨Fin.cycleRange 2, Fin.isThreeCycle_cycleRange_two.mem_alternatingGroup⟩) (inv_mem h) :)) - -/-- The normal closure of $(04)(13)$ within $A_5$ is the whole group. This will be -used to show that the normal closure of any permutation of cycle type $(2,2)$ is the whole group. --/ -theorem normalClosure_swap_mul_swap_five : - normalClosure - ({⟨swap 0 4 * swap 1 3, mem_alternatingGroup.2 (by decide)⟩} : - Set (alternatingGroup (Fin 5))) = - ⊤ := by - let g1 := (⟨swap 0 2 * swap 0 1, mem_alternatingGroup.2 (by decide)⟩ : alternatingGroup (Fin 5)) - let g2 := (⟨swap 0 4 * swap 1 3, mem_alternatingGroup.2 (by decide)⟩ : alternatingGroup (Fin 5)) - have h5 : g1 * g2 * g1⁻¹ * g2⁻¹ = - ⟨finRotate 5, finRotate_bit1_mem_alternatingGroup (n := 2)⟩ := by - rw [Subtype.ext_iff] - simp only [Subgroup.coe_mul, Subgroup.coe_inv] - decide - rw [eq_top_iff, ← normalClosure_finRotate_five] - refine normalClosure_le_normal ?_ - rw [Set.singleton_subset_iff, SetLike.mem_coe, ← h5] - have h : g2 ∈ normalClosure {g2} := - SetLike.mem_coe.1 (subset_normalClosure (Set.mem_singleton _)) - exact mul_mem (Subgroup.normalClosure_normal.conj_mem _ h g1) (inv_mem h) - set_option linter.flexible false in -- TODO: fix non-terminal simp /-- Shows that any non-identity element of $A_5$ whose cycle decomposition consists only of swaps is conjugate to $(04)(13)$. This is used to show that the normal closure of such a permutation in $A_5$ is $A_5$. -/ +@[deprecated "This was an auxilliary lemma for the proof of simplicity of A_5 which has now been +superceded by `alternatingGroup.isSimpleGroup`." (since := "2026-04-28")] theorem isConj_swap_mul_swap_of_cycleType_two {g : Perm (Fin 5)} (ha : g ∈ alternatingGroup (Fin 5)) (h1 : g ≠ 1) (h2 : ∀ n, n ∈ cycleType (g : Perm (Fin 5)) → n = 2) : IsConj (swap 0 4 * swap 1 3) g := by @@ -401,56 +348,6 @@ theorem isConj_swap_mul_swap_of_cycleType_two {g : Perm (Fin 5)} (ha : g ∈ alt decide · contradiction -/-- Shows that $A_5$ is simple by taking an arbitrary non-identity element and showing by casework - on its cycle type that its normal closure is all of $A_5$. -/ -instance isSimpleGroup_five : IsSimpleGroup (alternatingGroup (Fin 5)) := - ⟨fun H => by - intro Hn - refine or_not.imp id fun Hb => ?_ - rw [eq_bot_iff_forall] at Hb - push Not at Hb - obtain ⟨⟨g, gA⟩, gH, g1⟩ : ∃ x : ↥(alternatingGroup (Fin 5)), x ∈ H ∧ x ≠ 1 := Hb - -- `g` is a non-identity alternating permutation in a normal subgroup `H` of $A_5$. - rw [← SetLike.mem_coe, ← Set.singleton_subset_iff] at gH - refine eq_top_iff.2 (le_trans (ge_of_eq ?_) (normalClosure_le_normal gH)) - -- It suffices to show that the normal closure of `g` in $A_5$ is $A_5$. - by_cases h2 : ∀ n ∈ g.cycleType, n = 2 - -- If the cycle decomposition of `g` consists entirely of swaps, then the cycle type is $(2,2)$. - -- This means that it is conjugate to $(04)(13)$, whose normal closure is $A_5$. - · rw [Ne, Subtype.ext_iff] at g1 - exact - (isConj_swap_mul_swap_of_cycleType_two gA g1 h2).normalClosure_eq_top_of - normalClosure_swap_mul_swap_five - push Not at h2 - obtain ⟨n, ng, n2⟩ : ∃ n : ℕ, n ∈ g.cycleType ∧ n ≠ 2 := h2 - -- `n` is the size of a non-swap cycle in the decomposition of `g`. - have n2' : 2 < n := lt_of_le_of_ne (two_le_of_mem_cycleType ng) n2.symm - have n5 : n ≤ 5 := le_trans ?_ g.support.card_le_univ - -- We check that `2 < n ≤ 5`, so that `interval_cases` has a precise range to check. - swap - · obtain ⟨m, hm⟩ := Multiset.exists_cons_of_mem ng - rw [← sum_cycleType, hm, Multiset.sum_cons] - exact le_add_right le_rfl - interval_cases n - -- This breaks into cases `n = 3`, `n = 4`, `n = 5`. - -- If `n = 3`, then `g` has a 3-cycle in its decomposition, so `g^2` is a 3-cycle. - -- `g^2` is in the normal closure of `g`, so that normal closure must be $A_5$. - · rw [eq_top_iff, ← (isThreeCycle_sq_of_three_mem_cycleType_five ng).alternating_normalClosure - (by rw [Nat.card_fin])] - refine normalClosure_le_normal ?_ - rw [Set.singleton_subset_iff, SetLike.mem_coe] - have h := SetLike.mem_coe.1 (subset_normalClosure - (G := alternatingGroup (Fin 5)) (Set.mem_singleton ⟨g, gA⟩)) - exact mul_mem h h - · -- The case `n = 4` leads to contradiction, as no element of $A_5$ includes a 4-cycle. - have con := mem_alternatingGroup.1 gA - rw [sign_of_cycleType, cycleType_of_card_le_mem_cycleType_add_two (by decide) ng] at con - have : Odd 5 := by decide - simp [this] at con - · -- If `n = 5`, then `g` is itself a 5-cycle, conjugate to `finRotate 5`. - refine (isConj_iff_cycleType_eq.2 ?_).normalClosure_eq_top_of normalClosure_finRotate_five - rw [cycleType_of_card_le_mem_cycleType_add_two (by decide) ng, cycleType_finRotate]⟩ - theorem center_eq_bot (hα4 : 4 ≤ Nat.card α) : Subgroup.center (alternatingGroup α) = ⊥ := by rw [eq_bot_iff] diff --git a/Mathlib/GroupTheory/SpecificGroups/Alternating/Simple.lean b/Mathlib/GroupTheory/SpecificGroups/Alternating/Simple.lean index f373dc3cfb..a17e8afefd 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Alternating/Simple.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Alternating/Simple.lean @@ -64,7 +64,7 @@ This file contains two uncomfortable uses of `convert`: open scoped Pointwise -open MulAction Equiv.Perm Equiv Set.powersetCard +open MulAction Equiv.Perm Equiv Set.powersetCard Subgroup namespace Equiv.Perm @@ -212,4 +212,30 @@ public theorem isSimpleGroup (hα : 5 ≤ Nat.card α) : simpa using le_trans (by norm_num) hα eq_bot_or_eq_top_of_normal H _ := normal_subgroup_eq_bot_or_eq_top hα +@[deprecated "Use `alternatingGroup.isSimpleGroup` instead." (since := "2026-04-28")] +theorem _root_.Equiv.Perm.IsThreeCycle.alternating_normalClosure + (h5 : 5 ≤ Nat.card α) {f : Perm α} (hf : IsThreeCycle f) : + normalClosure ({⟨f, hf.mem_alternatingGroup⟩} : Set (alternatingGroup α)) = ⊤ := by + have : IsSimpleGroup (alternatingGroup α) := isSimpleGroup h5 + apply normalClosure_normal.eq_bot_or_eq_top.resolve_left + simp [hf.ne_one] + +@[deprecated "Use `alternatingGroup.isSimpleGroup` instead." (since := "2026-04-28")] +theorem normalClosure_finRotate_five : normalClosure ({⟨finRotate 5, + finRotate_bit1_mem_alternatingGroup (n := 2)⟩} : Set (alternatingGroup (Fin 5))) = ⊤ := by + have : IsSimpleGroup (alternatingGroup (Fin 5)) := isSimpleGroup (by simp) + apply normalClosure_normal.eq_bot_or_eq_top.resolve_left + simp +decide + +@[deprecated "Use `alternatingGroup.isSimpleGroup` instead." (since := "2026-04-28")] +theorem normalClosure_swap_mul_swap_five : normalClosure ({⟨swap 0 4 * swap 1 3, + mem_alternatingGroup.2 (by decide)⟩} : Set (alternatingGroup (Fin 5))) = ⊤ := by + have : IsSimpleGroup (alternatingGroup (Fin 5)) := isSimpleGroup (by simp) + apply normalClosure_normal.eq_bot_or_eq_top.resolve_left + simp +decide + +@[deprecated "Use `alternatingGroup.isSimpleGroup` instead." (since := "2026-04-28")] +instance isSimpleGroup_five : IsSimpleGroup (alternatingGroup (Fin 5)) := + isSimpleGroup (by simp) + end alternatingGroup From 6f105f74abe99cd70c28ffaabdbe3e347150eacb Mon Sep 17 00:00:00 2001 From: Thomas Browning <13339017+tb65536@users.noreply.github.com> Date: Sun, 10 May 2026 10:59:51 +0000 Subject: [PATCH 47/65] feat(GroupTheory/Torsion): add `torsion_eq_top_iff` (#38718) The torsion subgroup is the whole group if and only if the group is torsion. Co-authored-by: tb65536 --- Mathlib/GroupTheory/Torsion.lean | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mathlib/GroupTheory/Torsion.lean b/Mathlib/GroupTheory/Torsion.lean index 72dc27723d..d35e92dddc 100644 --- a/Mathlib/GroupTheory/Torsion.lean +++ b/Mathlib/GroupTheory/Torsion.lean @@ -299,6 +299,10 @@ theorem torsion_eq_torsion_submonoid : CommMonoid.torsion G = (torsion G).toSubm @[to_additive] theorem mem_torsion (g : G) : g ∈ torsion G ↔ IsOfFinOrder g := Iff.rfl +@[to_additive] +lemma torsion_eq_top_iff : torsion G = ⊤ ↔ IsTorsion G := + (torsion G).eq_top_iff' + @[to_additive] lemma isMulTorsionFree_iff_torsion_eq_bot : IsMulTorsionFree G ↔ CommGroup.torsion G = ⊥ := by rw [isMulTorsionFree_iff_not_isOfFinOrder, eq_bot_iff, SetLike.le_def] From 154aaf09f6328e1821af688f1f6658a03b4d7f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Violeta=20Hern=C3=A1ndez=20Palacios?= Date: Sun, 10 May 2026 10:59:53 +0000 Subject: [PATCH 48/65] feat: `DirSupClosedOn` is preserved under union (#38807) --- Mathlib/Order/DirSupClosed.lean | 81 +++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/Mathlib/Order/DirSupClosed.lean b/Mathlib/Order/DirSupClosed.lean index 521e2cc330..e1cb895c7c 100644 --- a/Mathlib/Order/DirSupClosed.lean +++ b/Mathlib/Order/DirSupClosed.lean @@ -167,6 +167,87 @@ lemma DirSupInaccOn.union (hs : DirSupInaccOn D s) (ht : DirSupInaccOn D t) : lemma DirSupInacc.union (hs : DirSupInacc s) (ht : DirSupInacc t) : DirSupInacc (s ∪ t) := by simpa using hs.dirSupInaccOn.union ht.dirSupInaccOn (D := .univ) +theorem DirSupClosedOn.union (hDL : IsLowerSet D) + (hs : DirSupClosedOn D s) (ht : DirSupClosedOn D t) : DirSupClosedOn D (s ∪ t) := by + intro d hD hdu hd₀ hd₁ a ha + have hdst : d ∩ s ∪ d ∩ t = d := by grind + rw [← hdst] at hd₀ hd₁ + wlog h : DirectedOn (· ≤ ·) (d ∩ s) ∧ (d ∩ s).Nonempty + · rw [union_comm] at hdu hd₀ hd₁ hdst ⊢ + exact this hDL ht hs hD hdu hd₀ hd₁ ha hdst <| + (directedOn_or_directedOn_of_union' hd₀ hd₁).resolve_right h + obtain ⟨hds, hn⟩ := h + by_cases had : a ∈ lowerBounds (upperBounds (d ∩ s)) + · exact .inl <| hs (hDL inter_subset_left hD) inter_subset_right hn hds + ⟨fun b hb ↦ ha.1 hb.1, had⟩ + · simp only [lowerBounds, mem_setOf_eq, not_forall] at had + obtain ⟨b, hb, hb'⟩ := had + have key : {x ∈ d | ¬ x ≤ b} ⊆ d ∩ t := fun a ⟨had, hab⟩ ↦ + ⟨had, (hdu had).resolve_left fun has ↦ hab <| hb ⟨had, has⟩⟩ + obtain ⟨w, hw⟩ : {x ∈ d | ¬ x ≤ b}.Nonempty := by + contrapose! hb' + apply ha.2 + aesop + refine Or.inr <| ht (hDL inter_subset_left hD) (key.trans inter_subset_right) + ⟨w, hw⟩ (fun x hx y hy ↦ ?_) ?_ + · obtain ⟨z, hz, hz'⟩ := hd₁ _ (.inr (key hx)) _ (.inr (key hy)) + exact ⟨z, ⟨⟨hdst ▸ hz, mt hz'.1.trans hx.2⟩, hz'⟩⟩ + · refine ⟨fun x hx ↦ ha.1 hx.1, fun x hx ↦ ha.2 fun y hy ↦ ?_⟩ + by_cases hyb : y ≤ b + · obtain ⟨z, hz, hxz, hyz⟩ := hd₁ _ (hdst ▸ hy) _ (.inr (key hw)) + exact hxz.trans (hx ⟨hdst ▸ hz, fun hzb ↦ hw.2 (hyz.trans hzb)⟩) + exact hx ⟨hy, hyb⟩ + +theorem DirSupInaccOn.inter (hDL : IsLowerSet D) + (hs : DirSupInaccOn D s) (ht : DirSupInaccOn D t) : DirSupInaccOn D (s ∩ t) := by + rw [← dirSupClosedOn_compl, compl_inter]; exact hs.compl.union hDL ht.compl + +theorem DirSupClosed.union (hs : DirSupClosed s) (ht : DirSupClosed t) : DirSupClosed (s ∪ t) := by + simpa using hs.dirSupClosedOn.union isLowerSet_univ ht.dirSupClosedOn + +theorem DirSupInacc.inter (hs : DirSupInacc s) (ht : DirSupInacc t) : DirSupInacc (s ∩ t) := by + simpa using hs.dirSupInaccOn.inter isLowerSet_univ ht.dirSupInaccOn + +theorem DirSupInaccOn.of_inter_subset + (h : ∀ ⦃d : Set α⦄, d ∈ D → d.Nonempty → DirectedOn (· ≤ ·) d → + ∀ ⦃a : α⦄, IsLUB d a → a ∈ s → ∃ b ∈ d, Ici b ∩ d ⊆ s) : DirSupInaccOn D s := by + intro d hd₀ hd₁ hd₂ a hda hd₃ + obtain ⟨b, hbd, hb⟩ := h hd₀ hd₁ hd₂ hda hd₃ + exact ⟨b, hbd, hb ⟨le_rfl, hbd⟩⟩ + +theorem DirSupInacc.of_inter_subset + (h : ∀ ⦃d : Set α⦄, d.Nonempty → DirectedOn (· ≤ ·) d → + ∀ ⦃a : α⦄, IsLUB d a → a ∈ s → ∃ b ∈ d, Ici b ∩ d ⊆ s) : DirSupInacc s := + dirSupInaccOn_univ.1 (.of_inter_subset (by simpa)) + +/-- The condition `(d ∩ s).Nonempty` in `DirSupInaccOn` can be replaced with the stronger +`∃ b ∈ d, Ici b ∩ d ⊆ s` (under mild assumptions on `D`). -/ +theorem dirSupInaccOn_iff_inter_subset (hDL : IsLowerSet D) : + DirSupInaccOn D s ↔ ∀ ⦃d : Set α⦄, d ∈ D → d.Nonempty → DirectedOn (· ≤ ·) d → + ∀ ⦃a : α⦄, IsLUB d a → a ∈ s → ∃ b ∈ d, Ici b ∩ d ⊆ s where + mpr := .of_inter_subset + mp h t hD ht₀ ht₁ a ha has := by + by_contra! H + have H : ∀ b : t, ∃ c, b.1 ≤ c ∧ c ∈ t ∧ c ∉ s := by simpa [not_subset, and_assoc] using H + choose f hf using H + have := ht₀.to_subtype + have hft : range f ⊆ t := by grind + apply (h (hDL hft hD) (range_nonempty f) _ _ has).ne_empty + · aesop + · intro a ha b hb + obtain ⟨c, hc, _, _⟩ := ht₁ _ (hft ha) _ (hft hb) + have := hf ⟨c, hc⟩ + grind + · exact ⟨upperBounds_mono_set hft ha.1, + fun b hb ↦ ha.2 fun c hc ↦ (hf ⟨c, hc⟩).1.trans (hb <| by simp)⟩ + +/-- The condition `(d ∩ s).Nonempty` in `DirSupInacc` can be replaced with the stronger +`∃ b ∈ d, Ici b ∩ d ⊆ s`. -/ +theorem dirSupInacc_iff_inter_subset : + DirSupInacc s ↔ ∀ ⦃d : Set α⦄, d.Nonempty → DirectedOn (· ≤ ·) d → + ∀ ⦃a : α⦄, IsLUB d a → a ∈ s → ∃ b ∈ d, Ici b ∩ d ⊆ s := by + simpa using dirSupInaccOn_iff_inter_subset isLowerSet_univ + lemma IsUpperSet.dirSupClosed (hs : IsUpperSet s) : DirSupClosed s := fun _d hds ⟨_b, hb⟩ _ _a ha ↦ hs (ha.1 hb) <| hds hb From 9086a8665825cbe3920f668a39471fc9b24c712d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 10:59:55 +0000 Subject: [PATCH 49/65] feat(AlgebraicTopology/ModelCategory): precylinders in full subcategories (#38998) This PR adds basic API for constructing precylinder or pre-path objects in full subcategories and study left/right homotopies in full subcategories. --- .../ModelCategory/Cylinder.lean | 22 +++++++++++++++++++ .../ModelCategory/LeftHomotopy.lean | 18 +++++++++++++++ .../ModelCategory/PathObject.lean | 22 +++++++++++++++++++ .../ModelCategory/RightHomotopy.lean | 18 +++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/Mathlib/AlgebraicTopology/ModelCategory/Cylinder.lean b/Mathlib/AlgebraicTopology/ModelCategory/Cylinder.lean index ca2f02a37d..9f681a5f76 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/Cylinder.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/Cylinder.lean @@ -106,6 +106,28 @@ end @[simp, reassoc] lemma symm_i [HasBinaryCoproducts C] : P.symm.i = (coprod.braiding A A).hom ≫ P.i := by cat_disch +/-- The precylinder in a full subcategory of `C` induced by a precylinder +in the category `C`. -/ +@[simps] +def toFullSubcategory {P : ObjectProperty C} {X : P.FullSubcategory} (Q : Precylinder X.obj) + (hQ : P Q.I) : + Precylinder X where + I := ⟨Q.I, hQ⟩ + i₀ := P.homMk Q.i₀ + i₁ := P.homMk Q.i₁ + π := P.homMk Q.π + +/-- The image of a precylinder by a functor. -/ +@[simps] +def map {X : C} (P : Precylinder X) {D : Type*} [Category* D] (F : C ⥤ D) : + Precylinder (F.obj X) where + I := F.obj P.I + i₀ := F.map P.i₀ + i₁ := F.map P.i₁ + π := F.map P.π + i₀_π := by simp [← F.map_comp] + i₁_π := by simp [← F.map_comp] + end Precylinder /-- In a category with weak equivalences, a cylinder is the diff --git a/Mathlib/AlgebraicTopology/ModelCategory/LeftHomotopy.lean b/Mathlib/AlgebraicTopology/ModelCategory/LeftHomotopy.lean index 99cebcb639..896925077c 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/LeftHomotopy.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/LeftHomotopy.lean @@ -82,6 +82,24 @@ def postcomp {f g : X ⟶ Y} (h : P.LeftHomotopy f g) {Z : C} (p : Y ⟶ Z) : P.LeftHomotopy (f ≫ p) (g ≫ p) where h := h.h ≫ p +/-- Left homotopies in a full subcategory identify to left homotopies in the +ambient category. -/ +noncomputable def fullSubcategoryEquiv {P : ObjectProperty C} {X Y : P.FullSubcategory} + {Q : Precylinder X} {f g : X ⟶ Y} : + Q.LeftHomotopy f g ≃ (Q.map P.ι).LeftHomotopy f.hom g.hom where + toFun h := + { h := h.h.hom + h₀ := by + dsimp + simp only [← h.h₀, ObjectProperty.FullSubcategory.comp_hom] + h₁ := by + dsimp + simp only [← h.h₁, ObjectProperty.FullSubcategory.comp_hom] } + invFun h := + { h := P.homMk h.h + h₀ := by ext; exact h.h₀ + h₁ := by ext; exact h.h₁ } + end LeftHomotopy end Precylinder diff --git a/Mathlib/AlgebraicTopology/ModelCategory/PathObject.lean b/Mathlib/AlgebraicTopology/ModelCategory/PathObject.lean index cfc9aa99a5..74e775549e 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/PathObject.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/PathObject.lean @@ -111,6 +111,28 @@ end lemma symm_p [HasBinaryProducts C] : P.symm.p = P.p ≫ (prod.braiding A A).hom := by aesop_cat +/-- The pre-path object in a full subcategory of `C` induced by a pre-path object +in the category `C`. -/ +@[simps] +def toFullSubcategory {P : ObjectProperty C} {X : P.FullSubcategory} (Q : PrepathObject X.obj) + (hQ : P Q.P) : + PrepathObject X where + P := ⟨Q.P, hQ⟩ + p₀ := P.homMk Q.p₀ + p₁ := P.homMk Q.p₁ + ι := P.homMk Q.ι + +/-- The image of a pre-path object by a functor. -/ +@[simps] +def map {X : C} (P : PrepathObject X) {D : Type*} [Category* D] (F : C ⥤ D) : + PrepathObject (F.obj X) where + P := F.obj P.P + p₀ := F.map P.p₀ + p₁ := F.map P.p₁ + ι := F.map P.ι + ι_p₀ := by simp [← F.map_comp] + ι_p₁ := by simp [← F.map_comp] + end PrepathObject /-- In a category with weak equivalences, a path object is the diff --git a/Mathlib/AlgebraicTopology/ModelCategory/RightHomotopy.lean b/Mathlib/AlgebraicTopology/ModelCategory/RightHomotopy.lean index 4850c25c86..51b005600e 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/RightHomotopy.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/RightHomotopy.lean @@ -85,6 +85,24 @@ def precomp {f g : X ⟶ Y} (h : P.RightHomotopy f g) {Z : C} (i : Z ⟶ X) : P.RightHomotopy (i ≫ f) (i ≫ g) where h := i ≫ h.h +/-- Right homotopies in a full subcategory identify to right homotopies in the +ambient category. -/ +noncomputable def fullSubcategoryEquiv {P : ObjectProperty C} {X Y : P.FullSubcategory} + {Q : PrepathObject Y} {f g : X ⟶ Y} : + Q.RightHomotopy f g ≃ (Q.map P.ι).RightHomotopy f.hom g.hom where + toFun h := + { h := h.h.hom + h₀ := by + dsimp + simp only [← h.h₀, ObjectProperty.FullSubcategory.comp_hom] + h₁ := by + dsimp + simp only [← h.h₁, ObjectProperty.FullSubcategory.comp_hom] } + invFun h := + { h := P.homMk h.h + h₀ := by ext; exact h.h₀ + h₁ := by ext; exact h.h₁ } + end RightHomotopy end PrepathObject From ea680d11653b1b27e390972859a25b5e9797a1b1 Mon Sep 17 00:00:00 2001 From: Eric Wieser <425260+eric-wieser@users.noreply.github.com> Date: Sun, 10 May 2026 11:46:26 +0000 Subject: [PATCH 50/65] feat(Algebra/Colimit/DirectLimit): nonunital algebra instances (#39017) --- Mathlib/Algebra/Colimit/DirectLimit.lean | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Mathlib/Algebra/Colimit/DirectLimit.lean b/Mathlib/Algebra/Colimit/DirectLimit.lean index 3c8edf2185..5c33f2bbbe 100644 --- a/Mathlib/Algebra/Colimit/DirectLimit.lean +++ b/Mathlib/Algebra/Colimit/DirectLimit.lean @@ -538,6 +538,24 @@ instance [Semiring R] [∀ i, AddCommMonoid (G i)] [∀ i, Module R (G i)] { add_smul _ _ := DirectLimit.induction _ fun i _ ↦ by simp_rw [smul_def, add_smul, add_def], zero_smul := DirectLimit.induction _ fun i _ ↦ by simp_rw [smul_def, zero_smul, zero_def i] } +instance [∀ i, Mul (G i)] [∀ i, SMul R (G i)] [∀ i, IsScalarTower R (G i) (G i)] + [∀ i j h, MulHomClass (T h) (G i) (G j)] [∀ i j h, MulActionHomClass (T h) R (G i) (G j)] : + IsScalarTower R (DirectLimit G f) (DirectLimit G f) where + smul_assoc r := DirectLimit.induction₂ _ fun i _ _ ↦ by + simp_rw [smul_eq_mul, smul_def, mul_def, smul_def, smul_mul_assoc] + +instance [∀ i, Mul (G i)] [∀ i, SMul R (G i)] [∀ i, SMulCommClass R (G i) (G i)] + [∀ i j h, MulHomClass (T h) (G i) (G j)] [∀ i j h, MulActionHomClass (T h) R (G i) (G j)] : + SMulCommClass R (DirectLimit G f) (DirectLimit G f) where + smul_comm r := DirectLimit.induction₂ _ fun i _ _ ↦ by + simp_rw [smul_eq_mul, smul_def, mul_def, smul_def, mul_smul_comm] + +instance [∀ i, Mul (G i)] [∀ i, SMul R (G i)] [∀ i, SMulCommClass (G i) R (G i)] + [∀ i j h, MulHomClass (T h) (G i) (G j)] [∀ i j h, MulActionHomClass (T h) R (G i) (G j)] : + SMulCommClass (DirectLimit G f) R (DirectLimit G f) := + have _ (i) : SMulCommClass R (G i) (G i) := SMulCommClass.symm _ _ _ + SMulCommClass.symm _ _ _ + end Action section DivisionSemiring From 4514e7baebbdbfc61ca874da566a973ba2407e1a Mon Sep 17 00:00:00 2001 From: Kevin Buzzard Date: Sun, 10 May 2026 12:06:52 +0000 Subject: [PATCH 51/65] perf(AlgebraicGeometry/EllipticCurve/Affine/Point): help elaborator (#39124) For some reason Lean likes it much better when we explicitly tell it `W`, even though it can be figured out through elaboration. Typeclass inference goes off on a completely unnecessary wild goose chase before this figuring-out is done, for some reason. --- .../EllipticCurve/Affine/Point.lean | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean index 117f2ad7e9..f1b4331644 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean @@ -377,16 +377,21 @@ lemma XYIdeal'_eq {x y : F} (h : W.Nonsingular x y) : (XYIdeal' h : FractionalIdeal W.CoordinateRing⁰ W.FunctionField) = XYIdeal W x (C y) := rfl +-- note: giving `W` to `XYIdeal'` explicitly hugely speeds up elaboration for some reason. +-- see https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Field.20.28FunctionField.20.3Fm.2E19.29/near/594011283 lemma mk_XYIdeal'_neg_mul {x y : F} (h : W.Nonsingular x y) : - ClassGroup.mk (XYIdeal' <| (nonsingular_neg ..).mpr h) * ClassGroup.mk (XYIdeal' h) = 1 := by + ClassGroup.mk (XYIdeal' (W := W) <| (nonsingular_neg ..).mpr h) * + ClassGroup.mk (XYIdeal' (W := W) h) = 1 := by rw [← map_mul] exact (ClassGroup.mk_eq_one_of_coe_ideal <| (coeIdeal_mul ..).symm.trans <| FractionalIdeal.coeIdeal_inj.mpr <| XYIdeal_neg_mul h).mpr ⟨_, XClass_ne_zero x, rfl⟩ +-- note: giving `W` to `XYIdeal'` explicitly hugely speeds up elaboration for some reason. +-- see https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Field.20.28FunctionField.20.3Fm.2E19.29/near/594011283 lemma mk_XYIdeal'_mul_mk_XYIdeal' [DecidableEq F] {x₁ x₂ y₁ y₂ : F} (h₁ : W.Nonsingular x₁ y₁) (h₂ : W.Nonsingular x₂ y₂) (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : - ClassGroup.mk (XYIdeal' h₁) * ClassGroup.mk (XYIdeal' h₂) = - ClassGroup.mk (XYIdeal' <| nonsingular_add h₁ h₂ hxy) := by + ClassGroup.mk (XYIdeal' (W := W) h₁) * ClassGroup.mk (XYIdeal' (W := W) h₂) = + ClassGroup.mk (XYIdeal' (W := W) <| nonsingular_add h₁ h₂ hxy) := by rw [← map_mul] exact (ClassGroup.mk_eq_mk_of_coe_ideal (coeIdeal_mul ..).symm <| XYIdeal'_eq _).mpr ⟨_, _, XClass_ne_zero _, YClass_ne_zero _, XYIdeal_mul_XYIdeal h₁.left h₂.left hxy⟩ @@ -723,8 +728,10 @@ noncomputable def toClass : W.Point →+ Additive (ClassGroup W.CoordinateRing) lemma toClass_zero : toClass (0 : W.Point) = 0 := rfl +-- note: giving `W` to `XYIdeal'` explicitly hugely speeds up elaboration for some reason. +-- see https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/Field.20.28FunctionField.20.3Fm.2E19.29/near/594011283 lemma toClass_some {x y : F} (h : W.Nonsingular x y) : - toClass (some _ _ h) = ClassGroup.mk (CoordinateRing.XYIdeal' h) := + toClass (some _ _ h) = ClassGroup.mk (CoordinateRing.XYIdeal' (W := W) h) := rfl private lemma add_eq_zero (P Q : W.Point) : P + Q = 0 ↔ P = -Q := by From 114285494bb0983b4c363d8698e64b121fdec496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 12:22:30 +0000 Subject: [PATCH 52/65] feat(CategoryTheory/Triangulated): localizing subcategories (#38651) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We show that the functor from the Verdier quotient `A/(A ⊓ B)` to `C/B` is fully faithful when `A` is a `B`-right- or left-localizing triangulated subcategory in the sense of Verdier. --- .../Triangulated/LocalizingSubcategory.lean | 197 +++++++++++++++++- 1 file changed, 194 insertions(+), 3 deletions(-) diff --git a/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean b/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean index 05308db5f8..9c8d3d42ec 100644 --- a/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean +++ b/Mathlib/CategoryTheory/Triangulated/LocalizingSubcategory.lean @@ -15,9 +15,9 @@ Let `C` be a pretriangulated category. If `A` and `B` are triangulated subcategories of `C`, we define predicates (typeclasses `IsVerdierRightLocalizing` and `IsVerdierLeftLocalizing`) saying that `A` is right `B`-localizing (or left `B`-localizing). -When `B` is closed under isomorphisms, we shall show that this implies that +When `B` is closed under isomorphisms, we show that this implies that the functor from the Verdier quotient `A/(A ⊓ B)` to `C/B` is fully -faithful (TODO @joelriou). +faithful. ## References * [Jean-Louis Verdier, *Des catégories dérivées des catégories abéliennes*, @@ -33,7 +33,7 @@ open Category Limits Pretriangulated Opposite namespace ObjectProperty -variable {C D D' : Type*} [Category* C] [Category* D] [Category* D'] +variable {C D D₁ D₂ : Type*} [Category* C] [Category* D] [Category* D₁] [Category* D₂] /-- If `A` and `B` are triangulated subcategories of a (pre)triangulated category `C` (with `B` closed under isomorphisms), we say that `A` is @@ -151,6 +151,197 @@ lemma IsVerdierLeftLocalizing.fac' ∃ (Z : C) (s' : Z ⟶ Y) (a : Z ⟶ X), A Z ∧ (A ⊓ B).trW s' ∧ a ≫ s = s' := (isVerdierLeftLocalizing_iff A B).1 inferInstance s hY hs +/-- If `A` is a triangulated subcategory of a pretriangulated category `C`, +and `B : ObjectProperty C`, this is the inclusion functor +`A.ι : A.FullSubcategory ⥤ C`, considered as a localizer morphism, +where `C` is equipped with the property of morphisms `B.trW` +and `A.FullSubcategory` with the property of morphisms `(B.inverseImage A.ι).trW`. -/ +@[implicit_reducible] +def triangulatedLocalizerMorphism [A.IsTriangulated] : + LocalizerMorphism (B.inverseImage A.ι).trW B.trW where + functor := A.ι + map X Y f hf := by + simp only [MorphismProperty.inverseImage_iff, trW_iff] at hf ⊢ + obtain ⟨Z, a, b, hT, hZ⟩ := hf + exact ⟨_, _, _, A.ι.map_distinguished _ hT, hZ⟩ + +instance [A.IsTriangulated] : + (triangulatedLocalizerMorphism A B).functor.CommShift ℤ := + inferInstanceAs (A.ι.CommShift ℤ) + +instance [A.IsTriangulated] : + (triangulatedLocalizerMorphism A B).functor.IsTriangulated := + inferInstanceAs A.ι.IsTriangulated + +lemma trW_inverseImage_ι_iff [A.IsTriangulated] {X Y : A.FullSubcategory} (f : X ⟶ Y) : + (B.inverseImage A.ι).trW f ↔ (A ⊓ B).trW f.hom := by + simp only [trW_iff] + constructor + · rintro ⟨Z, a, b, h, hZ⟩ + exact ⟨_, _, _, A.ι.map_distinguished _ h, Z.property, hZ⟩ + · rintro ⟨Z, a, b, h, hZ⟩ + refine ⟨⟨Z, hZ.1⟩, A.homMk a, A.homMk (b ≫ (A.ι.commShiftIso 1).inv.app _), ?_, hZ.2⟩ + rw [← A.ι.map_distinguished_iff] + refine isomorphic_distinguished _ h _ + (Triangle.isoMk _ _ (Iso.refl _) (Iso.refl _) (Iso.refl _) ?_ ?_ ?_) + · cat_disch + · cat_disch + · simp [dsimp% (A.ι.commShiftIso (1 : ℤ)).inv_hom_id_app X] + +lemma inverseImage_opEquivalence_inverse_trW_inverseImage_ι_op [A.IsTriangulated] + [B.IsTriangulated] [B.IsClosedUnderIsomorphisms] : + (B.op.inverseImage A.op.ι).trW.inverseImage A.opEquivalence.inverse = + (B.inverseImage A.ι).op.trW := by + ext ⟨X₁⟩ ⟨X₂⟩ a + simp [trW_op, trW_inverseImage_ι_iff, ← op_inf] + +variable [IsTriangulated C] [A.IsTriangulated] [B.IsTriangulated] [B.IsClosedUnderIsomorphisms] + +section + +variable [A.IsVerdierRightLocalizing B] + (L₁ : A.FullSubcategory ⥤ D₁) (L₂ : C ⥤ D₂) + [L₁.IsLocalization (B.inverseImage A.ι).trW] [L₂.IsLocalization B.trW] + +instance : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Full := by + let F := (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ + have : L₁.EssSurj := Localization.essSurj L₁ (B.inverseImage A.ι).trW + let e : A.ι ⋙ L₂ ≅ L₁ ⋙ F := CatCommSq.iso + (A.triangulatedLocalizerMorphism B).functor L₁ L₂ F + refine F.full_of_comp_essSurj L₁ (fun X₁ X₂ φ ↦ ?_) + obtain ⟨φ', hφ'⟩ : ∃ φ', φ = e.inv.app X₁ ≫ φ' ≫ e.hom.app X₂ := + ⟨e.hom.app X₁ ≫ φ ≫ e.inv.app X₂, by + simp [dsimp% e.inv_hom_id_app_assoc, dsimp% e.inv_hom_id_app]⟩ + obtain ⟨f, hf⟩ := Localization.exists_leftFraction L₂ B.trW φ' + obtain ⟨X₃, s', a, hX₃, hs', fac⟩ := + IsVerdierRightLocalizing.fac' f.s X₂.property f.hs + let g : (B.inverseImage A.ι).trW.LeftFraction X₁ X₂ := + { Y' := ⟨X₃, hX₃⟩ + f := A.homMk (f.f ≫ a) + s := A.homMk s' + hs := by rwa [trW_inverseImage_ι_iff] } + have := Localization.inverts L₁ _ _ g.hs + refine ⟨g.map L₁ (Localization.inverts _ _), ?_⟩ + rw [← cancel_mono (F.map (L₁.map g.s)), ← Functor.map_comp, + MorphismProperty.LeftFraction.map_comp_map_s] + simp [g, ← fac, hφ', hf, ← dsimp% NatIso.naturality_1 e, + dsimp% e.hom_inv_id_app_assoc] + +instance [Preadditive D₁] [Preadditive D₂] [L₁.Additive] [L₂.Additive] : + ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Additive := by + let F := (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ + rw [Localization.functor_additive_iff L₁ (B.inverseImage A.ι).trW] + let e : A.ι ⋙ L₂ ≅ L₁ ⋙ F := CatCommSq.iso + (A.triangulatedLocalizerMorphism B).functor L₁ L₂ F + exact Functor.additive_of_iso e + +instance : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Faithful := by + letI := Localization.preadditive L₁ (B.inverseImage A.ι).trW + letI := Localization.preadditive L₂ B.trW + have := Localization.functor_additive L₁ (B.inverseImage A.ι).trW + have := Localization.functor_additive L₂ B.trW + let F := (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ + let e : A.ι ⋙ L₂ ≅ L₁ ⋙ F := + CatCommSq.iso (A.triangulatedLocalizerMorphism B).functor L₁ L₂ F + refine Functor.faithful_of_comp_cancel_zero_of_hasLeftCalculusOfFractions L₁ + (B.inverseImage A.ι).trW F (fun X₁ X₂ f hf ↦ ?_) + replace hf : L₂.map f.hom = L₂.map 0 := by + simp [← dsimp% NatIso.naturality_2 e f, hf] + rw [MorphismProperty.map_eq_iff_postcomp L₂ B.trW] at hf + obtain ⟨X₃, s, hs, fac⟩ := hf + obtain ⟨X₄, t, a, hX₄, ht, fac'⟩ := + IsVerdierRightLocalizing.fac' s X₂.property hs + let t' : X₂ ⟶ ⟨X₄, hX₄⟩ := A.homMk t + have := Localization.inverts L₁ (B.inverseImage A.ι).trW t' + (by rwa [trW_inverseImage_ι_iff]) + rw [← cancel_mono (L₁.map t'), zero_comp, ← L₁.map_comp, ← L₁.map_zero] + congr 1 + ext + simp [t', ← fac', reassoc_of% fac] + +end + +instance [A.IsVerdierRightLocalizing B] : + (A.triangulatedLocalizerMorphism B).IsLocalizedFullyFaithful where + nonempty_fullyFaithful := ⟨.ofFullyFaithful _⟩ + +instance [A.IsVerdierLeftLocalizing B] : + (A.triangulatedLocalizerMorphism B).IsLocalizedFullyFaithful := by + let L₁ := (B.inverseImage A.ι).trW.Q + let L₂ := B.trW.Q + let F : (B.inverseImage A.ι).trW.Localization ⥤ B.trW.Localization := + (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ + letI : CatCommSq (A.op.triangulatedLocalizerMorphism B.op).functor + (A.opEquivalence.functor ⋙ L₁.op) L₂.op F.op := + ⟨Functor.isoWhiskerLeft A.opEquivalence.functor + (NatIso.op (CatCommSq.iso (A.triangulatedLocalizerMorphism B).functor L₁ L₂ F).symm)⟩ + have : L₂.op.IsLocalization B.op.trW := by rw [trW_op]; infer_instance + have : (A.opEquivalence.functor ⋙ L₁.op).IsLocalization (B.op.inverseImage A.op.ι).trW := by + refine Functor.IsLocalization.of_equivalence_source L₁.op (B.inverseImage A.ι).trW.op + _ _ A.opEquivalence.symm ?_ ?_ + ((Functor.associator _ _ _).symm ≪≫ + Functor.isoWhiskerRight A.opEquivalence.counitIso _ ≪≫ Functor.leftUnitor _) + · rw [← trW_op, ← inverseImage_opEquivalence_inverse_trW_inverseImage_ι_op] + intro _ _ f hf + simp only [MorphismProperty.inverseImage_iff, Equivalence.symm_functor] at hf ⊢ + exact MorphismProperty.le_isoClosure _ _ hf + · refine fun _ _ _ hf ↦ Localization.inverts L₁.op (B.inverseImage A.ι).trW.op _ ?_ + simpa [trW_inverseImage_ι_iff, ← op_inf, trW_op] using hf + exact LocalizerMorphism.IsLocalizedFullyFaithful.mk' (A.triangulatedLocalizerMorphism B) + L₁ L₂ F (((A.op.triangulatedLocalizerMorphism B.op).fullyFaithful + (A.opEquivalence.functor ⋙ L₁.op) L₂.op F.op).unop) + +section + +variable [A.IsVerdierLeftLocalizing B] (L₁ : A.FullSubcategory ⥤ D₁) (L₂ : C ⥤ D₂) + [L₁.IsLocalization (B.inverseImage A.ι).trW] + [L₂.IsLocalization B.trW] + +example : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Full := by + infer_instance + +example : ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Faithful := by + infer_instance + +instance [A.IsVerdierLeftLocalizing B] [Preadditive D₁] [Preadditive D₂] + [L₁.Additive] [L₂.Additive] : + ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂).Additive := by + let F := (A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂ + rw [Localization.functor_additive_iff L₁ (B.inverseImage A.ι).trW] + let e : A.ι ⋙ L₂ ≅ L₁ ⋙ F := CatCommSq.iso + (A.triangulatedLocalizerMorphism B).functor L₁ L₂ F + exact Functor.additive_of_iso e + +/-- If `A` is a left `B`-localizing triangulated subcategory in the sense of Verdier, +then the induced functor between the localizations with respect to `(B.inverseImage A.ι).trW` +and `B.trW` is fully faithful. -/ +@[no_expose] +noncomputable def IsVerdierLeftLocalizing.fullyFaithful [A.IsVerdierLeftLocalizing B] + {L₁ : A.FullSubcategory ⥤ D₁} {L₂ : C ⥤ D₂} {F : D₁ ⥤ D₂} + [L₁.IsLocalization (B.inverseImage A.ι).trW] [L₂.IsLocalization B.trW] + (e : L₁ ⋙ F ≅ A.ι ⋙ L₂) : + F.FullyFaithful := + Functor.FullyFaithful.ofIso (.ofFullyFaithful + ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂)) + (Localization.liftNatIso L₁ (B.inverseImage A.ι).trW + ((A.triangulatedLocalizerMorphism B).functor ⋙ L₂) (L₁ ⋙ F) _ _ e.symm) + +/-- If `A` is a right `B`-localizing triangulated subcategory in the sense of Verdier, +then the induced functor between the localizations with respect to `(B.inverseImage A.ι).trW` +and `B.trW` is fully faithful. -/ +@[no_expose] +noncomputable def IsVerdierRightLocalizing.fullyFaithful [A.IsVerdierRightLocalizing B] + {L₁ : A.FullSubcategory ⥤ D₁} {L₂ : C ⥤ D₂} {F : D₁ ⥤ D₂} + [L₁.IsLocalization (B.inverseImage A.ι).trW] [L₂.IsLocalization B.trW] + (e : L₁ ⋙ F ≅ A.ι ⋙ L₂) : + F.FullyFaithful := + Functor.FullyFaithful.ofIso (.ofFullyFaithful + ((A.triangulatedLocalizerMorphism B).localizedFunctor L₁ L₂)) + (Localization.liftNatIso L₁ (B.inverseImage A.ι).trW + ((A.triangulatedLocalizerMorphism B).functor ⋙ L₂) (L₁ ⋙ F) _ _ e.symm) + +end + end ObjectProperty end CategoryTheory From be95839f6eadf4fa4e1321693c225d974b2c5638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 12:34:16 +0000 Subject: [PATCH 53/65] feat(AlgebraicTopology/SimplicialSet): characterization of Kan complexes (#38665) --- .../SimplicialSet/CategoryWithFibrations.lean | 39 ++++++++++++- .../SimplicialSet/KanComplex.lean | 55 ++++++++++++++++++- 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/CategoryWithFibrations.lean b/Mathlib/AlgebraicTopology/SimplicialSet/CategoryWithFibrations.lean index 5b55870981..f0b22c83c1 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/CategoryWithFibrations.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/CategoryWithFibrations.lean @@ -7,7 +7,7 @@ module public import Mathlib.AlgebraicTopology.ModelCategory.CategoryWithCofibrations public import Mathlib.AlgebraicTopology.SimplicialSet.Boundary -public import Mathlib.AlgebraicTopology.SimplicialSet.Horn +public import Mathlib.AlgebraicTopology.SimplicialSet.HornColimits public import Mathlib.CategoryTheory.MorphismProperty.LiftingProperty /-! @@ -103,4 +103,41 @@ end end modelCategoryQuillen +namespace horn.IsCompatible + +open modelCategoryQuillen + +variable {X : SSet.{u}} {n : ℕ} + {i : Fin (n + 2)} {f : ∀ (j : Fin (n + 2)) (_ : j ≠ i), Δ[n] ⟶ X} + (hf : horn.IsCompatible f) {Y : SSet.{u}} (p : X ⟶ Y) [Fibration p] + (b : Δ[n + 1] ⟶ Y) + (comm : ∀ (j : Fin (n + 2)) (hj : j ≠ i), f j hj ≫ p = stdSimplex.δ j ≫ b) + +include hf comm in +lemma exists_lift : + ∃ (φ : Δ[n + 1] ⟶ X), + (∀ (j : Fin (n + 2)) (hj : j ≠ i), stdSimplex.δ j ≫ φ = f j hj) ∧ + φ ≫ p = b := by + have sq : CommSq hf.desc Λ[n + 1, i].ι p b := + ⟨horn.hom_ext' (fun j hj ↦ by simpa using comm j hj)⟩ + exact ⟨sq.lift, fun j hj ↦ by simp [← ι_ι_assoc i j hj], by simp⟩ + +/-- If `f : ∀ (j : Fin (n + 2)) (_ : j ≠ i), Δ[n] ⟶ X` is a compatible family +of morphisms (which defines a morphism `Λ[n + 1, i] ⟶ X`), `p : X ⟶ Y` a Kan fibration +and `b : Δ[n + 1] ⟶ Y` such that for all `j ≠ i`, `f j _ ≫ p = stdSimplex.δ j ≫ b`, +then this is a lifting `Δ[n + 1] ⟶ X`. -/ +@[no_expose] +noncomputable def lift : Δ[n + 1] ⟶ X := (hf.exists_lift p b comm).choose + +@[reassoc] +lemma δ_lift (j : Fin (n + 2)) (hj : j ≠ i := by grind) : + stdSimplex.δ j ≫ hf.lift p b comm = f j hj := + ((hf.exists_lift p b comm).choose_spec).1 j hj + +@[reassoc (attr := simp)] +lemma lift_comp : hf.lift p b comm ≫ p = b := + ((hf.exists_lift p b comm).choose_spec).2 + +end horn.IsCompatible + end SSet diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/KanComplex.lean b/Mathlib/AlgebraicTopology/SimplicialSet/KanComplex.lean index b17a458bee..202e392a62 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/KanComplex.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/KanComplex.lean @@ -31,7 +31,7 @@ universe u namespace SSet -open CategoryTheory Simplicial Limits +open CategoryTheory Simplicial Limits HomotopicalAlgebra open modelCategoryQuillen in /-- A simplicial set `S` is a Kan complex if it is fibrant, which means that @@ -47,4 +47,57 @@ lemma KanComplex.hornFilling {S : SSet.{u}} [KanComplex S] have sq' : CommSq σ₀ Λ[n + 1, i].ι (terminal.from S) (terminal.from _) := ⟨by simp⟩ exact ⟨sq'.lift, by simp⟩ +namespace horn.IsCompatible + +variable {X : SSet.{u}} {n : ℕ} + {i : Fin (n + 2)} {f : ∀ (j : Fin (n + 2)) (_ : j ≠ i), Δ[n] ⟶ X} + +lemma exists_lift_of_kanComplex [KanComplex X] + (hf : horn.IsCompatible f) : + ∃ (φ : Δ[n + 1] ⟶ X), + ∀ (j : Fin (n + 2)) (hj : j ≠ i), stdSimplex.δ j ≫ φ = f j hj := by + obtain ⟨φ, hφ, _⟩ := hf.exists_lift (terminal.from _) (terminal.from _) (by simp) + exact ⟨φ, hφ⟩ + +/-- If `X` is a Kan complex and `f : ∀ (j : Fin (n + 2)) (_ : j ≠ i), Δ[n] ⟶ X` +is a compatible family of morphisms (which defines a morphism `Λ[n + 1, i] ⟶ X`), +then this is a lifting `Δ[n + 1] ⟶ X`. -/ +@[no_expose] +noncomputable def liftOfKanComplex [KanComplex X] (hf : horn.IsCompatible f) : + Δ[n + 1] ⟶ X := + hf.exists_lift_of_kanComplex.choose + +@[reassoc] +lemma δ_liftOfKanComplex [KanComplex X] (hf : horn.IsCompatible f) + (j : Fin (n + 2)) (hj : j ≠ i := by grind) : + stdSimplex.δ j ≫ hf.liftOfKanComplex = f j hj := + hf.exists_lift_of_kanComplex.choose_spec j hj + +end horn.IsCompatible + +open modelCategoryQuillen in +/-- A simplicial set `X` is a Kan complex iff for any `n : ℕ`, `i : Fin (n + 2)`, +and any family of morphisms `Δ[n] ⟶ Z` for all `j ≠ i` that is compatible +(in the sense that it extends to a morphism `Λ[n + 1, i] ⟶ X`), there +exists a morphism `Δ[n + 1] ⟶ Z` which induces the given family of morphisms +on the faces `j ≠ i`. -/ +lemma KanComplex.iff {Z : SSet.{u}} : + KanComplex Z ↔ + ∀ ⦃n : ℕ⦄ ⦃i : Fin (n + 2)⦄ (f : ∀ (j : Fin (n + 2)) (_ : j ≠ i), Δ[n] ⟶ Z) + (_ : horn.IsCompatible f), + ∃ (φ : Δ[n + 1] ⟶ Z), + ∀ (j : Fin (n + 2)) (hj : j ≠ i), stdSimplex.δ j ≫ φ = f j hj := by + refine ⟨fun _ n i f hf ↦ hf.exists_lift_of_kanComplex, + fun h ↦ (isFibrant_iff _).2 ⟨?_⟩⟩ + rw [fibrations_eq] + intro _ _ _ hf + simp only [J, MorphismProperty.iSup_iff] at hf + obtain ⟨n, ⟨i⟩⟩ := hf + refine ⟨fun {t _} _ ↦ ?_⟩ + obtain ⟨φ, hφ⟩ := h _ (horn.IsCompatible.of_hom t) + exact ⟨⟨{ + l := φ + fac_left := horn.hom_ext' (by simpa using hφ) + fac_right := by subsingleton }⟩⟩ + end SSet From 1b6244ba2a57456b4206043ea8907b7a0180a670 Mon Sep 17 00:00:00 2001 From: Christian Merten <136261474+chrisflav@users.noreply.github.com> Date: Sun, 10 May 2026 12:46:53 +0000 Subject: [PATCH 54/65] feat(AlgebraicGeometry): rank of finite flat morphism (#38090) From Pi1. --- Mathlib.lean | 1 + Mathlib/Algebra/Algebra/Tower.lean | 2 +- Mathlib/AlgebraicGeometry/AffineScheme.lean | 5 + .../AlgebraicGeometry/Morphisms/Affine.lean | 14 + .../AlgebraicGeometry/Morphisms/Finite.lean | 6 + .../Morphisms/FinitePresentation.lean | 10 + Mathlib/AlgebraicGeometry/Morphisms/Flat.lean | 8 + .../AlgebraicGeometry/Morphisms/FlatRank.lean | 299 ++++++++++++++++++ .../Shapes/Pullback/IsPullback/Basic.lean | 44 +++ Mathlib/RingTheory/Flat/Rank.lean | 66 +++- .../RingTheory/Localization/BaseChange.lean | 31 ++ 11 files changed, 484 insertions(+), 2 deletions(-) create mode 100644 Mathlib/AlgebraicGeometry/Morphisms/FlatRank.lean diff --git a/Mathlib.lean b/Mathlib.lean index 74c3baa467..2ef8198250 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -1379,6 +1379,7 @@ public import Mathlib.AlgebraicGeometry.Morphisms.FiniteType public import Mathlib.AlgebraicGeometry.Morphisms.Flat public import Mathlib.AlgebraicGeometry.Morphisms.FlatDescent public import Mathlib.AlgebraicGeometry.Morphisms.FlatMono +public import Mathlib.AlgebraicGeometry.Morphisms.FlatRank public import Mathlib.AlgebraicGeometry.Morphisms.FormallyUnramified public import Mathlib.AlgebraicGeometry.Morphisms.Immersion public import Mathlib.AlgebraicGeometry.Morphisms.Integral diff --git a/Mathlib/Algebra/Algebra/Tower.lean b/Mathlib/Algebra/Algebra/Tower.lean index f02b2ee027..9b1be4777b 100644 --- a/Mathlib/Algebra/Algebra/Tower.lean +++ b/Mathlib/Algebra/Algebra/Tower.lean @@ -405,7 +405,7 @@ section Algebra.algebraMapSubmonoid @[simp] theorem Algebra.algebraMapSubmonoid_map_map {R A B : Type*} [CommSemiring R] [CommSemiring A] - [Algebra R A] (M : Submonoid R) [CommRing B] [Algebra R B] [Algebra A B] [IsScalarTower R A B] : + [Algebra R A] (M : Submonoid R) [Semiring B] [Algebra R B] [Algebra A B] [IsScalarTower R A B] : algebraMapSubmonoid B (algebraMapSubmonoid A M) = algebraMapSubmonoid B M := algebraMapSubmonoid_map_eq _ (IsScalarTower.toAlgHom R A B) diff --git a/Mathlib/AlgebraicGeometry/AffineScheme.lean b/Mathlib/AlgebraicGeometry/AffineScheme.lean index 0c784ad637..6d88881da5 100644 --- a/Mathlib/AlgebraicGeometry/AffineScheme.lean +++ b/Mathlib/AlgebraicGeometry/AffineScheme.lean @@ -274,6 +274,11 @@ theorem exists_isAffineOpen_mem_and_subset {X : Scheme.{u}} {x : X} exact ⟨Scheme.Hom.opensRange f (H := hf.1), ⟨AlgebraicGeometry.isAffineOpen_opensRange f (H := hf.1), hf.2.1, hf.2.2⟩⟩ +lemma Scheme.exists_Spec_apply_eq {X : Scheme.{u}} (x : X) : + ∃ (R : CommRingCat.{u}) (f : Spec R ⟶ X) (_ : IsOpenImmersion f) (y : Spec R), + f.base y = x := + ⟨X.affineOpenCover.X _, X.affineOpenCover.f _, inferInstance, X.affineOpenCover.covers x⟩ + instance Scheme.isAffine_affineCover (X : Scheme) (i : X.affineCover.I₀) : IsAffine (X.affineCover.X i) := isAffine_Spec _ diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean b/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean index 5a61725e13..648670f27f 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Affine.lean @@ -200,6 +200,20 @@ instance {X Y S : Scheme} (f : X ⟶ S) (g : Y ⟶ S) [IsAffineHom g] [IsAffine letI : IsAffineHom (pullback.fst f g) := MorphismProperty.pullback_fst _ _ ‹_› isAffine_of_isAffineHom (pullback.fst f g) +lemma IsAffine.of_isPullback {P : Scheme.{u}} {fst : P ⟶ X} {snd : P ⟶ Y} {f : X ⟶ Z} {g : Y ⟶ Z} + [IsAffine X] [IsAffineHom g] (h : IsPullback fst snd f g) : + IsAffine P := + .of_isIso h.isoPullback.hom + +lemma isPushout_appTop_of_isPullback {P : Scheme.{u}} {fst : P ⟶ X} {snd : P ⟶ Y} {f : X ⟶ Z} + {g : Y ⟶ Z} [IsAffine X] [IsAffine Y] [IsAffine Z] (h : IsPullback fst snd f g) : + IsPushout f.appTop g.appTop fst.appTop snd.appTop := by + have : IsAffine P := .of_isPullback h + have : IsPullback (AffineScheme.ofHom fst) (AffineScheme.ofHom snd) (AffineScheme.ofHom f) + (AffineScheme.ofHom g) := + IsPullback.of_map_of_faithful AffineScheme.forgetToScheme.{u} h + exact (IsPullback.map AffineScheme.Γ.rightOp this).unop.flip + set_option backward.isDefEq.respectTransparency false in instance {U V X : Scheme.{u}} (f : U ⟶ X) (g : V ⟶ X) [IsAffineHom f] [IsAffineHom g] : IsAffineHom (coprod.desc f g) := by diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean index 1374d1d091..86a83253b5 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean @@ -64,6 +64,7 @@ instance : ContainsIdentities @IsFinite := instance : IsMultiplicative @IsFinite where +@[simp] lemma SpecMap_iff {R S : CommRingCat.{u}} (f : R ⟶ S) : IsFinite (Spec.map f) ↔ f.hom.Finite := by rw [HasAffineProperty.iff_of_isAffine (P := @IsFinite), and_iff_right (by infer_instance), @@ -153,6 +154,11 @@ instance {U V X : Scheme.{u}} (f : U ⟶ X) (g : V ⟶ X) [IsFinite f] [IsFinite end IsFinite +lemma Scheme.Hom.finite_appTop {X Y : Scheme.{u}} (f : X ⟶ Y) [IsAffine X] [IsAffine Y] + [IsFinite f] : + f.appTop.hom.Finite := + (HasAffineProperty.iff_of_isAffine (P := @IsFinite).mp inferInstance).2 + /-- If `X` is a Jacobson scheme and `k` is a field, `Spec(k) ⟶ X` is finite iff it is (locally) of finite type. (The statement is more general to allow the empty scheme as well) -/ diff --git a/Mathlib/AlgebraicGeometry/Morphisms/FinitePresentation.lean b/Mathlib/AlgebraicGeometry/Morphisms/FinitePresentation.lean index a2f6479c47..20b222fa62 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/FinitePresentation.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/FinitePresentation.lean @@ -67,6 +67,16 @@ instance (priority := 900) locallyOfFinitePresentation_of_isOpenImmersion [IsOpe instance : MorphismProperty.IsStableUnderComposition @LocallyOfFinitePresentation := HasRingHomProperty.stableUnderComposition RingHom.finitePresentation_stableUnderComposition +@[simp] +lemma LocallyOfFinitePresentation.SpecMap_iff {R S : CommRingCat.{u}} (f : R ⟶ S) : + LocallyOfFinitePresentation (Spec.map f) ↔ f.hom.FinitePresentation := + HasRingHomProperty.Spec_iff + +lemma Scheme.Hom.finitePresentation_appTop {X Y : Scheme.{u}} (f : X ⟶ Y) [IsAffine X] [IsAffine Y] + [LocallyOfFinitePresentation f] : + f.appTop.hom.FinitePresentation := + HasRingHomProperty.appTop (P := @LocallyOfFinitePresentation) _ inferInstance + instance locallyOfFinitePresentation_comp {X Y Z : Scheme.{u}} (f : X ⟶ Y) (g : Y ⟶ Z) [hf : LocallyOfFinitePresentation f] [hg : LocallyOfFinitePresentation g] : LocallyOfFinitePresentation (f ≫ g) := diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean index 2461f84450..ea2689ea7b 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean @@ -63,6 +63,10 @@ instance (priority := 900) [IsOpenImmersion f] : Flat f := instance : MorphismProperty.IsStableUnderComposition @Flat := HasRingHomProperty.stableUnderComposition RingHom.Flat.stableUnderComposition +@[simp] +lemma SpecMap_iff {R S : CommRingCat.{u}} {f : R ⟶ S} : Flat (Spec.map f) ↔ f.hom.Flat := + HasRingHomProperty.Spec_iff + instance comp {X Y Z : Scheme} (f : X ⟶ Y) (g : Y ⟶ Z) [hf : Flat f] [hg : Flat g] : Flat (f ≫ g) := MorphismProperty.comp_mem _ f g hf hg @@ -166,6 +170,10 @@ lemma flat_and_surjective_iff_faithfullyFlat_of_isAffine [IsAffine X] [IsAffine end Flat +lemma Scheme.Hom.flat_appTop [IsAffine X] [IsAffine Y] [Flat f] : + f.appTop.hom.Flat := + HasRingHomProperty.appTop (P := @Flat) _ inferInstance + lemma flat_and_surjective_SpecMap_iff {R S : CommRingCat.{u}} (f : R ⟶ S) : Flat (Spec.map f) ∧ Surjective (Spec.map f) ↔ f.hom.FaithfullyFlat := by rw [HasRingHomProperty.Spec_iff (P := @Flat), diff --git a/Mathlib/AlgebraicGeometry/Morphisms/FlatRank.lean b/Mathlib/AlgebraicGeometry/Morphisms/FlatRank.lean new file mode 100644 index 0000000000..a56323f6ca --- /dev/null +++ b/Mathlib/AlgebraicGeometry/Morphisms/FlatRank.lean @@ -0,0 +1,299 @@ +/- +Copyright (c) 2025 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module + +public import Mathlib.CategoryTheory.Limits.Shapes.Countable +public import Mathlib.RingTheory.Finiteness.ModuleFinitePresentation +public import Mathlib.AlgebraicGeometry.Morphisms.Flat +public import Mathlib.AlgebraicGeometry.Morphisms.Finite +public import Mathlib.AlgebraicGeometry.Morphisms.FinitePresentation +public import Mathlib.RingTheory.Flat.Rank + +/-! +# Rank of a finite flat morphism of schemes + +In this file we define the rank `AlgebraicGeometry.Scheme.Hom.finrank` of a finite flat morphism of +schemes `f : X ⟶ Y`. It is locally constant and is characterized by the condition that the rank of +`Spec S ⟶ Spec R` at some prime `p` of `R` is the rank of `S` as an `R`-algebra at `p`. + +## Main definitions + +- `AlgebraicGeometry.Scheme.Hom.finrank`: For a morphism `f : X ⟶ Y` of schemes, the function + `Y → ℕ` sending `y` to the rank of `f_* 𝒪_X` over `𝒪_Y` at `y`. Instead of talking about + sheaves, we define it by choosing an open neighbourhood of `y`. + This is sometimes also called the degree of a morphism in the literature. + +## Main results + +- `AlgebraicGeometry.Scheme.Hom.isLocallyConstant_finrank`: The rank function of a finite flat + locally finitely presented morphism is locally constant. +- `AlgebraicGeometry.Scheme.Hom.one_le_finrank_iff_surjective`: The rank function is at least `1` + everywhere if and only if the morphism is surjective. +- `AlgebraicGeometry.Scheme.Hom.isIso_iff_finrank_eq`: A finite flat locally finitely presented + morphism is an isomorphism if and only if its rank is constant equal to `1`. + +## TODO + +- Relate `Hom.finrank f y` to the rank of `f_* 𝒪_X` over `𝒪_Y` at `y` when the API for + locally free sheaves of modules is developed. +-/ + +public section + +open CategoryTheory Limits TopologicalSpace TensorProduct + +universe u + +namespace AlgebraicGeometry + +noncomputable section + +variable {X S Y T : Scheme.{u}} (f : X ⟶ S) + +/-- The rank of a morphism `f : X ⟶ S` of schemes at a point `s : S`, when `S` is affine. +This is used as an auxiliary definition to define `AlgebraicGeometry.finrank`. -/ +private def IsAffine.finrank [IsAffine S] (f : X ⟶ S) (s : S) : ℕ := + f.appTop.hom.finrank (S.isoSpec.hom s) + +private lemma IsAffine.finrank_of_isPullback [IsAffine S] [IsAffine T] + (f' : Y ⟶ T) (g' : Y ⟶ X) (g : T ⟶ S) (h : IsPullback g' f' f g) [Flat f] [IsFinite f] + (s : S) (t : T) (hs : g t = s) : + IsAffine.finrank f' t = IsAffine.finrank f s := by + subst hs + have : IsAffine X := isAffine_of_isAffineHom f + have : IsPushout f.appTop g.appTop g'.appTop f'.appTop := isPushout_appTop_of_isPullback h + dsimp [finrank] + rw [CommRingCat.finrank_eq_of_isPushout this f.flat_appTop f.finite_appTop (T.isoSpec.hom t), + ← Scheme.Hom.comp_apply, ← Scheme.isoSpec_hom_naturality] + rfl + +private lemma IsAffine.finrank_snd [IsAffine S] [IsAffine T] + (g : T ⟶ S) [Flat f] [IsFinite f] (x : T) : + IsAffine.finrank (pullback.snd f g) x = IsAffine.finrank f (g x) := + finrank_of_isPullback f _ _ _ (.of_hasPullback _ _) _ _ rfl + +private lemma IsAffine.finrank_comp_left_of_isIso [IsAffine S] + (f : X ⟶ Y) (g : Y ⟶ S) [IsIso f] [IsFinite g] [Flat g] : + IsAffine.finrank (f ≫ g) = IsAffine.finrank g := by + ext z + apply finrank_of_isPullback g (f ≫ g) f (𝟙 _) _ _ _ rfl + exact IsPullback.of_horiz_isIso (by simp) + +/-- The rank of a morphism `f : X ⟶ S` of schemes at a point `s : S`. When `f` is finite, +flat and locally of finite presentation, this is a locally constant function (see +`AlgebraicGeometry.isLocallyConstant_finrank`). -/ +@[stacks 02KA "second part"] +def Scheme.Hom.finrank {X S : Scheme.{u}} (f : X ⟶ S) (s : S) : ℕ := + IsAffine.finrank (pullback.snd f (S.affineOpenCover.f <| S.affineOpenCover.idx s)) + (S.affineOpenCover.covers s).choose + +set_option backward.isDefEq.respectTransparency false in +private lemma Scheme.Hom.finrank_eq_finrank_snd_of_isAffine (g : T ⟶ S) [IsAffine T] (t : T) + [Flat f] [IsFinite f] : + f.finrank (g t) = IsAffine.finrank (pullback.snd f g) t := by + let i := S.affineOpenCover.f (S.affineOpenCover.idx (g t)) + obtain ⟨y, hyl, hyr⟩ := Scheme.Pullback.exists_preimage_pullback + (S.affineOpenCover.covers <| g t).choose t (S.affineOpenCover.covers <| g t).choose_spec + obtain ⟨R, u, hu, z, rfl⟩ := (pullback i g).exists_Spec_apply_eq y + trans IsAffine.finrank (pullback.snd (pullback.snd f g) (u ≫ pullback.snd _ _)) z + · refine (IsAffine.finrank_of_isPullback _ _ ?_ ?_ ?_ _ _ ?_).symm + · exact pullback.map _ _ _ _ (pullback.fst f g) (u ≫ pullback.fst _ _) g + pullback.condition.symm (by simp [← pullback.condition]; rfl) + · exact u ≫ pullback.fst _ _ + · apply IsPullback.map_fst_comp_fst_snd_comp_fst + · exact hyl + · simp_rw [← hyr] + exact IsAffine.finrank_snd (pullback.snd f g) (u ≫ pullback.snd _ _) z + +private lemma Scheme.Hom.finrank_eq_of_isAffine [IsAffine S] [Flat f] [IsFinite f] (s : S) : + f.finrank s = IsAffine.finrank f s := by + rw [show s = (𝟙 S : S ⟶ S) s from rfl, finrank_eq_finrank_snd_of_isAffine, + IsAffine.finrank_snd] + +@[simp] +lemma Scheme.Hom.finrank_SpecMap_eq_finrank {R S : CommRingCat.{u}} {f : R ⟶ S} (hf₁ : f.hom.Finite) + (hf₂ : f.hom.Flat) : + finrank (Spec.map f) = f.hom.finrank := by + simp only [← IsFinite.SpecMap_iff, ← Flat.SpecMap_iff] at hf₁ hf₂ + have hf₁ : (Spec.map f).appTop.hom.Finite := (Spec.map f).finite_appTop + have hf₂ : (Spec.map f).appTop.hom.Flat := (Spec.map f).flat_appTop + ext + rw [finrank_eq_of_isAffine, IsAffine.finrank] + have : f = (Scheme.ΓSpecIso R).inv ≫ (Spec.map f).appTop ≫ (Scheme.ΓSpecIso S).hom := by simp + conv_rhs => rw [this] + dsimp + rw [RingHom.finrank_comp_right_of_bijective _ _ (ConcreteCategory.bijective_of_isIso _)] + · rw [RingHom.finrank_comp_left_of_bijective _ _ (ConcreteCategory.bijective_of_isIso _) hf₁ hf₂] + · exact .comp (.of_surjective _ (ConcreteCategory.bijective_of_isIso _).surjective) hf₁ + · exact .comp hf₂ (.of_bijective (ConcreteCategory.bijective_of_isIso _)) + · simp [isoSpec_Spec_hom, SpecMap_ΓSpecIso_hom, ← AlgebraicGeometry.Spec.map_apply, + ← Scheme.Hom.comp_apply, toSpecΓ_SpecMap_ΓSpecIso_inv] + +lemma Scheme.Hom.finrank_SpecMap_algebraMap (R S : Type u) [CommRing R] [CommRing S] [Algebra R S] + [Module.Finite R S] [Module.Flat R S] (x : PrimeSpectrum R) : + finrank (Spec.map (CommRingCat.ofHom <| algebraMap R S)) x = Module.rankAtStalk S x := by + rw [finrank_SpecMap_eq_finrank] + · simp + · simpa [RingHom.finite_algebraMap] + · simpa [RingHom.flat_algebraMap_iff] + +variable (f : X ⟶ Y) [Flat f] [IsFinite f] + +@[simp] +lemma Scheme.Hom.finrank_comp_left_of_isIso (f : X ⟶ Y) (g : Y ⟶ S) + [IsIso f] [Flat g] [IsFinite g] : + finrank (f ≫ g) = finrank g := by + ext z + let e : pullback (f ≫ g) (S.affineOpenCover.f (S.affineOpenCover.idx z)) ≅ + pullback g (S.affineOpenCover.f (S.affineOpenCover.idx z)) := + (pullbackRightPullbackFstIso g (S.affineOpenCover.f (S.affineOpenCover.idx z)) f).symm ≪≫ + asIso (pullback.snd f (pullback.fst g (S.affineOpenCover.f _))) + have : e.hom ≫ pullback.snd _ _ = pullback.snd _ _ := by simp [e] + rw [finrank, finrank, ← this, IsAffine.finrank_comp_left_of_isIso] + +lemma Scheme.Hom.finrank_pullback_snd {Z : Scheme.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) + [Flat f] [IsFinite f] (y : Y) : + finrank (pullback.snd f g) y = finrank f (g y) := by + obtain ⟨R, i, _, y', rfl⟩ := Y.exists_Spec_apply_eq y + rw [← Scheme.Hom.comp_apply, finrank_eq_finrank_snd_of_isAffine, + finrank_eq_finrank_snd_of_isAffine, ← pullbackLeftPullbackSndIso_hom_snd f g i, + ← finrank_eq_of_isAffine, ← finrank_eq_of_isAffine, finrank_comp_left_of_isIso] + +lemma Scheme.Hom.finrank_of_isPullback {P X Y Z : Scheme.{u}} (fst : P ⟶ X) (snd : P ⟶ Y) + (f : X ⟶ Z) (g : Y ⟶ Z) (h : IsPullback fst snd f g) [Flat f] [IsFinite f] (y : Y) : + finrank snd y = finrank f (g y) := by + rw [← h.isoPullback_hom_snd, finrank_comp_left_of_isIso, finrank_pullback_snd] + +lemma Scheme.Hom.finrank_pullback_fst {Z : Scheme.{u}} (f : X ⟶ Z) (g : Y ⟶ Z) + [Flat f] [IsFinite f] (y : Y) : + finrank (pullback.fst g f) y = finrank f (g y) := + finrank_of_isPullback (pullback.snd g f) _ _ _ (.flip <| .of_hasPullback _ _) y + +nonrec lemma Scheme.Hom.one_le_finrank_map (x : X) : 1 ≤ finrank f (f x) := by + wlog hY : ∃ R, Y = Spec R + · obtain ⟨R, g, hg, y, hy⟩ := Y.exists_Spec_apply_eq (f x) + rw [← hy, ← finrank_pullback_snd] + obtain ⟨z, hzl, hzr⟩ := Scheme.Pullback.exists_preimage_pullback (f := f) (g := g) x y hy.symm + rw [hzr.symm] + refine this _ _ ⟨_, rfl⟩ + obtain ⟨R, rfl⟩ := hY + wlog hX : ∃ S, X = Spec S + · have _ : IsAffine X := isAffine_of_isAffineHom f + have heq : f x = (X.isoSpec.inv ≫ f) (X.isoSpec.hom x) := by simp + rw [← finrank_comp_left_of_isIso X.isoSpec.inv, heq] + exact this _ _ _ ⟨_, rfl⟩ + obtain ⟨S, rfl⟩ := hX + obtain ⟨φ, rfl⟩ := Spec.map_surjective f + simp only [IsFinite.SpecMap_iff, Flat.SpecMap_iff] at * + rw [finrank_SpecMap_eq_finrank ‹_› ‹_›] + algebraize [φ.hom] + rw [← RingHom.algebraMap_toAlgebra φ.hom, RingHom.finrank_algebraMap, Nat.add_one_le_iff, + PrimeSpectrum.rankAtStalk_pos_iff_mem_range_comap] + use x + rfl + +set_option backward.isDefEq.respectTransparency false in +/-- A finite flat locally finitely presented morphism is surjective if and only if its rank +function is at least `1` everywhere. -/ +nonrec lemma Scheme.Hom.one_le_finrank_iff_surjective : 1 ≤ finrank f ↔ Surjective f := by + refine ⟨fun h ↦ ?_, fun _ ↦ ?_⟩ + · wlog hY : ∃ R, Y = Spec R + · rw [IsZariskiLocalAtTarget.iff_of_openCover (P := @Surjective) Y.affineCover] + intro i + dsimp only [Scheme.Cover.pullbackHom] + refine this _ (fun y ↦ ?_) ⟨_, rfl⟩ + rw [finrank_pullback_snd] + exact h _ + obtain ⟨R, rfl⟩ := hY + wlog hX : ∃ S, X = Spec S + · have _ : IsAffine X := isAffine_of_isAffineHom f + rw [← MorphismProperty.cancel_left_of_respectsIso @Surjective X.isoSpec.inv] + refine this _ _ (fun x ↦ ?_) ⟨_, rfl⟩ + rw [finrank_comp_left_of_isIso] + exact h x + obtain ⟨S, rfl⟩ := hX + obtain ⟨φ, rfl⟩ := Spec.map_surjective f + constructor + intro x + specialize h x + simp only [IsFinite.SpecMap_iff, Flat.SpecMap_iff] at * + rw [finrank_SpecMap_eq_finrank ‹_› ‹_›] at h + algebraize [φ.hom] + exact (PrimeSpectrum.rankAtStalk_pos_iff_mem_range_comap _).mp h + · intro y + obtain ⟨x, rfl⟩ := f.surjective y + exact one_le_finrank_map f x + +/-- The rank of a finite flat locally finitely presented morphism is locally constant. -/ +nonrec lemma Scheme.Hom.isLocallyConstant_finrank [LocallyOfFinitePresentation f] : + IsLocallyConstant (finrank f) := by + wlog hY : ∃ R, Y = Spec R + · rw [IsLocallyConstant.iff_exists_open] + intro y + obtain ⟨R, g, _, x, rfl⟩ := Y.exists_Spec_apply_eq y + simp_rw [IsLocallyConstant.iff_exists_open] at this + obtain ⟨U, hU, hxU, H⟩ := this (pullback.snd f g) ⟨_, rfl⟩ x + refine ⟨g ''ᵁ ⟨U, hU⟩, (g ''ᵁ ⟨U, hU⟩).2, ⟨x, hxU, rfl⟩, fun y ↦ ?_⟩ + rintro ⟨y', (hyU : y' ∈ U), (rfl : g y' = y)⟩ + rw [← finrank_pullback_snd _ g, ← finrank_pullback_snd _ g] + exact H y' hyU + obtain ⟨R, rfl⟩ := hY + wlog hX : ∃ S, X = Spec S + · have _ : IsAffine X := isAffine_of_isAffineHom f + rw [← finrank_comp_left_of_isIso X.isoSpec.inv] + exact this _ _ ⟨_, rfl⟩ + obtain ⟨S, rfl⟩ := hX + obtain ⟨φ, rfl⟩ := Spec.map_surjective f + simp only [Flat.SpecMap_iff, IsFinite.SpecMap_iff, LocallyOfFinitePresentation.SpecMap_iff] at * + rw [finrank_SpecMap_eq_finrank ‹_› ‹_›] + algebraize [φ.hom] + have := Module.FinitePresentation.of_finite_of_finitePresentation + exact Module.isLocallyConstant_rankAtStalk + +set_option backward.isDefEq.respectTransparency false in +/-- The rank of an isomorphism is `1`. -/ +lemma Scheme.Hom.finrank_eq_one_of_isIso (f : X ⟶ Y) [IsIso f] : finrank f = 1 := by + ext y + obtain ⟨R, g, _, y, rfl⟩ := Y.exists_Spec_apply_eq y + have : Nontrivial R := y.nontrivial + rw [← finrank_pullback_snd, ← Category.comp_id (pullback.snd f g), finrank_comp_left_of_isIso, + ← Spec.map_id, finrank_SpecMap_eq_finrank, CommRingCat.hom_id, Pi.one_apply, + ← Algebra.algebraMap_self, RingHom.finrank_algebraMap] + · simp + · exact RingHom.Finite.id R + · exact RingHom.Flat.id ↑R + +/-- A finite flat locally finitely presented morphism is an isomorphism if and only if +its rank is constant equal to `1`. -/ +nonrec lemma Scheme.Hom.isIso_iff_finrank_eq : IsIso f ↔ finrank f = 1 := by + refine ⟨fun h ↦ finrank_eq_one_of_isIso f, fun h ↦ ?_⟩ + wlog hY : ∃ R, Y = Spec R + · rw [← MorphismProperty.isomorphisms.iff, + IsZariskiLocalAtTarget.iff_of_openCover (P := .isomorphisms Scheme) Y.affineCover] + intro i + dsimp [Scheme.Cover.pullbackHom] + refine this _ ?_ ⟨_, rfl⟩ + ext y + rw [finrank_pullback_snd, h, Pi.one_apply, Pi.one_apply] + obtain ⟨R, rfl⟩ := hY + wlog hX : ∃ S, X = Spec S + · have _ : IsAffine X := isAffine_of_isAffineHom f + rw [← isIso_comp_left_iff X.isoSpec.inv] + refine this _ _ ?_ ⟨_, rfl⟩ + rw [finrank_comp_left_of_isIso, h] + obtain ⟨S, rfl⟩ := hX + obtain ⟨φ, rfl⟩ := Spec.map_surjective f + simp only [IsFinite.SpecMap_iff, Flat.SpecMap_iff] at * + algebraize [φ.hom] + have : IsIso φ := by + rw [ConcreteCategory.isIso_iff_bijective] + apply Module.algebraMap_bijective_of_rankAtStalk + rwa [finrank_SpecMap_eq_finrank ‹_› ‹_›] at h + infer_instance + +end + +end AlgebraicGeometry diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean index a464ce8856..b278fd7e86 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean @@ -385,6 +385,50 @@ lemma mk' {P X Y Z : C} {fst : P ⟶ X} {snd : P ⟶ Y} {f : X ⟶ Z} {g : Y ⟶ (h₁.trans (l s).choose_spec.1.symm) (h₂.trans (l s).choose_spec.2.symm))⟩ +/-- +The main objects in this lemma fit in the following commutative diagram: +``` +Pfg -------> X <------- Pfi + | | | + | f | + ↓ ↓ ↓ + Y --- g --> S <-- i --- Z + \ / + -- -- + \ / + -- R -- +``` +Suppose the two squares are cartesian, then `Pfg ×[Y] R` is the pullback of +`Pfi ⟶ Z` and `R ⟶ Z`. +-/ +lemma paste_twist_right {X Y Z S : C} {f : X ⟶ S} {g : Y ⟶ S} {i : Z ⟶ S} + {Pfg : C} {fstfg : Pfg ⟶ X} {sndfg : Pfg ⟶ Y} (hfg : IsPullback fstfg sndfg f g) + {Pfi : C} {fstfi : Pfi ⟶ X} {sndfi : Pfi ⟶ Z} (hfi : IsPullback fstfi sndfi f i) + {R : C} (rY : R ⟶ Y) (rZ : R ⟶ Z) (hrw : rY ≫ g = rZ ≫ i) + {Psndfgr : C} (fstsndfgr : Psndfgr ⟶ Pfg) (sndsndfgr : Psndfgr ⟶ R) + (hsndfgr : IsPullback fstsndfgr sndsndfgr sndfg rY) + {t : Psndfgr ⟶ Pfi} (ht₁ : t ≫ fstfi = fstsndfgr ≫ fstfg) (ht₂ : t ≫ sndfi = sndsndfgr ≫ rZ) : + IsPullback t sndsndfgr sndfi rZ := by + refine .of_right ?_ ht₂ hfi + rw [← hrw, ht₁] + exact .paste_horiz hsndfgr hfg + +set_option backward.isDefEq.respectTransparency false in +/-- This is a `HasPullback` variant of `CategoryTheory.IsPullback.paste_twist_right` -/ +lemma map_fst_comp_fst_snd_comp_fst {X Y Z U S : C} (f : X ⟶ S) (g : Y ⟶ S) (i : Z ⟶ S) + [HasPullback i g] (h : U ⟶ pullback i g) [HasPullback f g] [HasPullback (pullback.snd f g) + (h ≫ pullback.snd i g)] [HasPullback f i] : + IsPullback + (pullback.map (pullback.snd f g) (h ≫ pullback.snd i g) f i (pullback.fst f g) + (h ≫ pullback.fst i g) g + pullback.condition.symm (by simp [pullback.condition])) + (pullback.snd (pullback.snd f g) (h ≫ pullback.snd i g)) + (pullback.snd f i) + (h ≫ pullback.fst i g) := + paste_twist_right (.of_hasPullback f g) (.of_hasPullback f i) (h ≫ pullback.snd _ _) + (h ≫ pullback.fst _ _) (by simp [pullback.condition]) _ _ (.of_hasPullback _ _) (by simp) + (by simp) + end IsPullback namespace IsPushout diff --git a/Mathlib/RingTheory/Flat/Rank.lean b/Mathlib/RingTheory/Flat/Rank.lean index 43401ac19c..7d35764a59 100644 --- a/Mathlib/RingTheory/Flat/Rank.lean +++ b/Mathlib/RingTheory/Flat/Rank.lean @@ -7,6 +7,7 @@ module public import Mathlib.LinearAlgebra.Trace public import Mathlib.RingTheory.Spectrum.Prime.FreeLocus +public import Mathlib.RingTheory.RingHom.Flat /-! @@ -34,7 +35,11 @@ open TensorProduct attribute [local instance] Module.free_of_flat_of_isLocalRing -variable {R S : Type*} [CommRing R] [CommRing S] [Algebra R S] [Module.Flat R S] [Module.Finite R S] +variable {R S : Type*} [CommRing R] [CommRing S] [Algebra R S] + +section + +variable [Module.Flat R S] [Module.Finite R S] lemma PrimeSpectrum.rankAtStalk_pos_iff_mem_range_comap (p : PrimeSpectrum R) : 0 < Module.rankAtStalk (R := R) S p ↔ p ∈ Set.range (PrimeSpectrum.comap (algebraMap R S)) := by @@ -122,3 +127,62 @@ lemma Module.algebraMap_bijective_iff_rankAtStalk : grind alias ⟨Module.algebraMap_bijective_of_rankAtStalk, _⟩ := Module.algebraMap_bijective_iff_rankAtStalk + +end + +section + +/-- The rank of a ring homomorphism `f : R →+* S` at a prime `x` of `R` is the rank of +`S` as an `R`-module at the stalk of `x`. -/ +@[expose] +noncomputable def RingHom.finrank {R S : Type*} [CommRing R] [CommRing S] (f : R →+* S) + (x : PrimeSpectrum R) : ℕ := + letI : Algebra R S := f.toAlgebra + Module.rankAtStalk S x + +@[simp] +lemma RingHom.finrank_algebraMap : + (algebraMap R S).finrank = Module.rankAtStalk (R := R) S := by + ext + rw [RingHom.finrank, toAlgebra_algebraMap] + +lemma Algebra.rankAtStalk_eq_of_isPushout (R S : Type*) [CommRing R] [CommRing S] [Algebra R S] + (R' S' : Type*) [CommRing R'] [CommRing S'] [Algebra R R'] [Algebra S S'] [Algebra R' S'] + [Algebra R S'] [IsScalarTower R R' S'] [IsScalarTower R S S'] + [Algebra.IsPushout R S R' S'] [Module.Flat R S] [Module.Finite R S] (x : PrimeSpectrum R') : + Module.rankAtStalk S' x = Module.rankAtStalk S (PrimeSpectrum.comap (algebraMap R R') x) := by + have : IsPushout R R' S S' := Algebra.IsPushout.symm inferInstance + have := Module.rankAtStalk_eq_of_equiv (Algebra.IsPushout.equiv R R' S S').symm.toLinearEquiv + rw [Module.rankAtStalk_eq_of_equiv (Algebra.IsPushout.equiv R R' S S').symm.toLinearEquiv, + Module.rankAtStalk_baseChange] + +lemma RingHom.finrank_comp_left_of_bijective {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] + (f : R →+* S) (g : S →+* T) (hf : Function.Bijective g) (h1 : f.Finite) (h2 : f.Flat) + (x : PrimeSpectrum R) : (g.comp f).finrank x = f.finrank x := by + algebraize [f, g, (g.comp f)] + have : Algebra.IsPushout R S R T := .of_bijective_right _ _ hf + apply Algebra.rankAtStalk_eq_of_isPushout + +attribute [local instance] Algebra.TensorProduct.rightAlgebra in +lemma RingHom.finrank_comp_right_of_bijective {R S T : Type*} [CommRing R] [CommRing S] + [CommRing T] (f : R →+* S) (g : S →+* T) (hg : Function.Bijective f) (h1 : g.Finite) + (h2 : g.Flat) (y : PrimeSpectrum R) (x : PrimeSpectrum S) + (hy : y = PrimeSpectrum.comap f x) : + (g.comp f).finrank y = g.finrank x := by + subst hy + algebraize [f, g, (g.comp f)] + have : Module.Finite R T := h1.comp <| .of_surjective _ hg.2 + have : Module.Flat R T := (RingHom.Flat.of_bijective hg).comp h2 + have : Algebra.IsPushout R T S T := .of_bijective_left _ _ hg + exact (Algebra.rankAtStalk_eq_of_isPushout _ _ _ _ _).symm + +lemma CommRingCat.finrank_eq_of_isPushout {R S T P : CommRingCat.{u}} {f : R ⟶ S} {g : R ⟶ T} + {inl : S ⟶ P} {inr : T ⟶ P} (h : CategoryTheory.IsPushout f g inl inr) (hf : f.hom.Flat) + (hfin : f.hom.Finite) (x : PrimeSpectrum T) : + inr.hom.finrank x = f.hom.finrank (PrimeSpectrum.comap g.hom x) := by + algebraize [f.hom, g.hom, inl.hom, inr.hom, inl.hom.comp f.hom] + have : IsScalarTower R T P := .of_algebraMap_eq' <| congr($(h.1.1).hom) + have : Algebra.IsPushout R S T P := CommRingCat.isPushout_iff_isPushout.mp h + exact Algebra.rankAtStalk_eq_of_isPushout R S T P x + +end diff --git a/Mathlib/RingTheory/Localization/BaseChange.lean b/Mathlib/RingTheory/Localization/BaseChange.lean index b308f049b6..fd34209bf8 100644 --- a/Mathlib/RingTheory/Localization/BaseChange.lean +++ b/Mathlib/RingTheory/Localization/BaseChange.lean @@ -165,6 +165,37 @@ lemma Algebra.isPushout_of_isLocalization [IsLocalization (Algebra.algebraMapSub Algebra.IsPushout R T A B := (Algebra.isLocalization_iff_isPushout S _).mp inferInstance +lemma Submonoid.map_isUnit_le_isUnit {M N : Type*} [Monoid M] [Monoid N] + {F : Type*} [FunLike F M N] [MonoidHomClass F M N] (f : F) : + Submonoid.map f (IsUnit.submonoid M) ≤ IsUnit.submonoid N := by + rintro x ⟨y, hy, rfl⟩ + exact hy.map _ + +lemma Algebra.algebraMapSubmonoid_isUnit_le_isUnit {R S : Type*} [CommSemiring R] [Semiring S] + [Algebra R S] : + Algebra.algebraMapSubmonoid S (IsUnit.submonoid R) ≤ IsUnit.submonoid S := by + rintro x ⟨y, hy, rfl⟩ + exact hy.map _ + +instance {R S : Type*} [CommSemiring R] [CommSemiring S] [Algebra R S] : + IsLocalization (Algebra.algebraMapSubmonoid S (IsUnit.submonoid R)) S := + IsLocalization.of_le_isUnit Algebra.algebraMapSubmonoid_isUnit_le_isUnit + +lemma Algebra.IsPushout.of_bijective_left [Algebra A T] [IsScalarTower R A T] + (H : Function.Bijective (algebraMap R A)) : + IsPushout R T A T := by + have : IsLocalization (IsUnit.submonoid R) A := + IsLocalization.of_le_isUnit_of_bijective Algebra.algebraMapSubmonoid_isUnit_le_isUnit H + apply isPushout_of_isLocalization (IsUnit.submonoid R) + +lemma Algebra.IsPushout.of_bijective_right [Algebra A T] [IsScalarTower R A T] + (H : Function.Bijective (algebraMap A T)) : + IsPushout R A R T := by + have : IsLocalization (algebraMapSubmonoid A (IsUnit.submonoid R)) T := by + apply IsLocalization.of_le_isUnit_of_bijective _ H + simpa using Algebra.algebraMapSubmonoid_isUnit_le + apply Algebra.isPushout_of_isLocalization (IsUnit.submonoid R) + variable (R M) in open TensorProduct in instance {α} [IsLocalizedModule S f] : From 879015f4bfcd1151ad3839e58c0e4c81dcf25501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ya=C3=ABl=20Dillies?= Date: Sun, 10 May 2026 13:25:24 +0000 Subject: [PATCH 55/65] feat(Analysis): the trivial inner product space (#39065) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide the `InnerProductSpace 𝕜 PUnit` instance. From MeanFourier --- Mathlib/Analysis/InnerProductSpace/Defs.lean | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Mathlib/Analysis/InnerProductSpace/Defs.lean b/Mathlib/Analysis/InnerProductSpace/Defs.lean index 87c6f5bb54..7048d51799 100644 --- a/Mathlib/Analysis/InnerProductSpace/Defs.lean +++ b/Mathlib/Analysis/InnerProductSpace/Defs.lean @@ -600,4 +600,17 @@ def InnerProductSpace.ofCoreOfTopology [AddCommGroup F] [hF : Module 𝕜 F] [To structure HilbertSpace (𝕜 E : Type*) [RCLike 𝕜] [NormedAddCommGroup E] [InnerProductSpace 𝕜 E] [CompleteSpace E] +namespace PUnit + +instance : InnerProductSpace 𝕜 PUnit where + inner _ _ := 0 + norm_sq_eq_re_inner := by simp + conj_inner_symm := by simp + add_left := by simp + smul_left := by simp + +@[simp] lemma inner_eq_zero (x y : PUnit) : inner 𝕜 x y = 0 := rfl + +end PUnit + end From 08fe4f24e3d818cf86b5615fdc9cbc5257480a39 Mon Sep 17 00:00:00 2001 From: Etienne Marion <66847262+EtienneC30@users.noreply.github.com> Date: Sun, 10 May 2026 13:51:23 +0000 Subject: [PATCH 56/65] feat: the cardinal of a finset is an ite sum over a bigger finset (#37831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If `s ⊆ t` then `s.card = ∑ i ∈ t, if i ∈ s then 1 else 0`. --- Mathlib/Algebra/BigOperators/Ring/Finset.lean | 3 +++ Mathlib/Data/Finset/BooleanAlgebra.lean | 3 +-- Mathlib/Data/Finset/Filter.lean | 5 +++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Mathlib/Algebra/BigOperators/Ring/Finset.lean b/Mathlib/Algebra/BigOperators/Ring/Finset.lean index 7b038d906a..e38c701f8e 100644 --- a/Mathlib/Algebra/BigOperators/Ring/Finset.lean +++ b/Mathlib/Algebra/BigOperators/Ring/Finset.lean @@ -45,6 +45,9 @@ lemma natCast_card_filter (p) [DecidablePred p] (s : Finset ι) : (∑ x ∈ s, if p x then 1 else 0 : R) = #{x ∈ s | p x} := (natCast_card_filter _ _).symm +lemma card_eq_sum_ite {s t : Finset ι} [DecidablePred (· ∈ s)] (hst : s ⊆ t) : + s.card = ∑ i ∈ t, if i ∈ s then 1 else 0 := by simp [hst] + end AddCommMonoidWithOne section NonUnitalNonAssocSemiring diff --git a/Mathlib/Data/Finset/BooleanAlgebra.lean b/Mathlib/Data/Finset/BooleanAlgebra.lean index 5719a71861..19e63b5c40 100644 --- a/Mathlib/Data/Finset/BooleanAlgebra.lean +++ b/Mathlib/Data/Finset/BooleanAlgebra.lean @@ -273,8 +273,7 @@ section DecEq variable [Fintype α] [DecidableEq α] -@[simp] -lemma filter_univ_mem (s : Finset α) : univ.filter (· ∈ s) = s := by simp [filter_mem_eq_inter] +lemma filter_univ_mem (s : Finset α) : univ.filter (· ∈ s) = s := by simp instance decidableCodisjoint : Decidable (Codisjoint s t) := decidable_of_iff _ codisjoint_left.symm diff --git a/Mathlib/Data/Finset/Filter.lean b/Mathlib/Data/Finset/Filter.lean index 205857319a..2efe5f0ac3 100644 --- a/Mathlib/Data/Finset/Filter.lean +++ b/Mathlib/Data/Finset/Filter.lean @@ -226,6 +226,11 @@ lemma filter_inj : s.filter p = t.filter p ↔ ∀ ⦃a⦄, p a → (a ∈ s ↔ lemma filter_inj' : s.filter p = s.filter q ↔ ∀ ⦃a⦄, a ∈ s → (p a ↔ q a) := by simp [Finset.ext_iff] +@[simp] +lemma filter_mem_eq_of_subset [DecidablePred (· ∈ s)] (hst : s ⊆ t) : + t.filter (· ∈ s) = s := by + grind + end Filter end Finset From 7d8b819974788a050de7963ae45a2021e32fc93f Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Sun, 10 May 2026 14:42:46 +0000 Subject: [PATCH 57/65] chore(Algebra/Module): remove some `backward.privateInPublic` (#38604) --- Mathlib/Algebra/Module/GradedModule.lean | 8 ++--- Mathlib/Algebra/Module/Submodule/Lattice.lean | 36 +++++++------------ 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/Mathlib/Algebra/Module/GradedModule.lean b/Mathlib/Algebra/Module/GradedModule.lean index 4a8a2e32e3..ef3b584f93 100644 --- a/Mathlib/Algebra/Module/GradedModule.lean +++ b/Mathlib/Algebra/Module/GradedModule.lean @@ -104,7 +104,6 @@ theorem of_smul_of [DecidableEq ιA] [DecidableEq ιB] [GMonoid A] [Gmodule A M] open AddMonoidHom -- Almost identical to the proof of `direct_sum.one_mul` -set_option backward.privateInPublic true in private theorem one_smul' [DecidableEq ιA] [DecidableEq ιB] [GMonoid A] [Gmodule A M] (x : ⨁ i, M i) : (1 : ⨁ i, A i) • x = x := by @@ -115,7 +114,6 @@ private theorem one_smul' [DecidableEq ιA] [DecidableEq ιB] [GMonoid A] [Gmodu exact DirectSum.of_eq_of_gradedMonoid_eq (one_smul (GradedMonoid A) <| GradedMonoid.mk i xi) -- Almost identical to the proof of `direct_sum.mul_assoc` -set_option backward.privateInPublic true in private theorem mul_smul' [DecidableEq ιA] [DecidableEq ιB] [GSemiring A] [Gmodule A M] (a b : ⨁ i, A i) (c : ⨁ i, M i) : (a * b) • c = a • b • c := by @@ -136,13 +134,11 @@ private theorem mul_smul' [DecidableEq ιA] [DecidableEq ιB] [GSemiring A] [Gmo DirectSum.of_eq_of_gradedMonoid_eq (mul_smul (GradedMonoid.mk ai ax) (GradedMonoid.mk bi bx) (GradedMonoid.mk ci cx)) -set_option backward.privateInPublic true in -set_option backward.privateInPublic.warn false in /-- The `Module` derived from `gmodule A M`. -/ instance module [DecidableEq ιA] [DecidableEq ιB] [GSemiring A] [Gmodule A M] : Module (⨁ i, A i) (⨁ i, M i) where - one_smul := one_smul' _ _ - mul_smul := mul_smul' _ _ + one_smul := by exact one_smul' _ _ + mul_smul := by exact mul_smul' _ _ smul_add r := (smulAddMonoidHom A M r).map_add smul_zero r := (smulAddMonoidHom A M r).map_zero add_smul r s x := by simp only [smul_def, map_add, AddMonoidHom.add_apply] diff --git a/Mathlib/Algebra/Module/Submodule/Lattice.lean b/Mathlib/Algebra/Module/Submodule/Lattice.lean index 0295f2eb9b..8f2375d830 100644 --- a/Mathlib/Algebra/Module/Submodule/Lattice.lean +++ b/Mathlib/Algebra/Module/Submodule/Lattice.lean @@ -184,14 +184,6 @@ instance : InfSet (Submodule R M) := add_mem' := by simp +contextual [add_mem] smul_mem' := by simp +contextual [smul_mem] }⟩ -set_option backward.privateInPublic true in -private theorem sInf_le' {S : Set (Submodule R M)} {p} : p ∈ S → sInf S ≤ p := - Set.biInter_subset_of_mem - -set_option backward.privateInPublic true in -private theorem le_sInf' {S : Set (Submodule R M)} {p} : (∀ q ∈ S, p ≤ q) → p ≤ sInf S := - Set.subset_iInter₂ - protected theorem isGLB_sInf {S : Set (Submodule R M)} : IsGLB S (sInf S) := .of_image SetLike.coe_subset_coe isGLB_biInf @@ -202,22 +194,18 @@ instance : Min (Submodule R M) := add_mem' := by simp +contextual [add_mem] smul_mem' := by simp +contextual [smul_mem] }⟩ -set_option backward.privateInPublic true in -set_option backward.privateInPublic.warn false in -instance completeLattice : CompleteLattice (Submodule R M) := - { (inferInstance : OrderTop (Submodule R M)), - (inferInstance : OrderBot (Submodule R M)) with - sup := fun a b ↦ sInf { x | a ≤ x ∧ b ≤ x } - le_sup_left := fun _ _ ↦ le_sInf' fun _ ⟨h, _⟩ ↦ h - le_sup_right := fun _ _ ↦ le_sInf' fun _ ⟨_, h⟩ ↦ h - sup_le := fun _ _ _ h₁ h₂ ↦ sInf_le' ⟨h₁, h₂⟩ - inf := (· ⊓ ·) - le_inf := fun _ _ _ ↦ Set.subset_inter - inf_le_left := fun _ _ ↦ Set.inter_subset_left - inf_le_right := fun _ _ ↦ Set.inter_subset_right - sSup S := sInf {sm | ∀ s ∈ S, s ≤ sm} - isLUB_sSup _ := isGLB_upperBounds.mp Submodule.isGLB_sInf - isGLB_sInf _ := Submodule.isGLB_sInf } +instance completeLattice : CompleteLattice (Submodule R M) where + sup a b := sInf { x | a ≤ x ∧ b ≤ x } + le_sup_left _ _ := Set.subset_iInter₂ fun _ ⟨h, _⟩ ↦ h + le_sup_right _ _ := Set.subset_iInter₂ fun _ ⟨_, h⟩ ↦ h + sup_le _ _ _ h₁ h₂ := Set.biInter_subset_of_mem ⟨h₁, h₂⟩ + inf := (· ⊓ ·) + le_inf _ _ _ := Set.subset_inter + inf_le_left _ _ := Set.inter_subset_left + inf_le_right _ _ := Set.inter_subset_right + sSup S := sInf {sm | ∀ s ∈ S, s ≤ sm} + isLUB_sSup _ := isGLB_upperBounds.mp Submodule.isGLB_sInf + isGLB_sInf _ := Submodule.isGLB_sInf @[simp] theorem coe_inf : ↑(p ⊓ q) = (p ∩ q : Set M) := From f71cc9dd706820e0a81e71e7da7f0965aee71c9b Mon Sep 17 00:00:00 2001 From: emlis <175462753+emlis42@users.noreply.github.com> Date: Sun, 10 May 2026 14:42:48 +0000 Subject: [PATCH 58/65] chore(NumberTheory/Divisors): golf `Int.mem_divisorsAntidiag` (#38889) This PR merges the case split and removes duplicated proofs in `Int.mem_divisorsAntidiag`. --- Mathlib/NumberTheory/Divisors.lean | 43 +++++------------------------- 1 file changed, 7 insertions(+), 36 deletions(-) diff --git a/Mathlib/NumberTheory/Divisors.lean b/Mathlib/NumberTheory/Divisors.lean index 964d4299a3..f618c25539 100644 --- a/Mathlib/NumberTheory/Divisors.lean +++ b/Mathlib/NumberTheory/Divisors.lean @@ -692,44 +692,15 @@ lemma mem_divisors_self (hz : z ≠ 0) : z ∈ divisors z := simp @[simp] -lemma mem_divisorsAntidiag : - ∀ {z} {xy : ℤ × ℤ}, xy ∈ divisorsAntidiag z ↔ xy.fst * xy.snd = z ∧ z ≠ 0 - | (n : ℕ), ((x : ℕ), (y : ℕ)) => by +lemma mem_divisorsAntidiag : xy ∈ divisorsAntidiag z ↔ xy.fst * xy.snd = z ∧ z ≠ 0 := by + rcases z, xy with ⟨_ | _, ⟨_ | _, _ | _⟩⟩ + -- splitting this case saves about 1770 heartbeats i.e. 12.5% faster + case ofNat.negSucc.negSucc => simp [divisorsAntidiag] - norm_cast - simp +contextual [eq_comm] - | (n : ℕ), (negSucc x, negSucc y) => by - simp [divisorsAntidiag, negSucc_eq, -neg_add_rev] - norm_cast - simp +contextual [eq_comm] - | (n : ℕ), ((x : ℕ), negSucc y) => by - simp [divisorsAntidiag, negSucc_eq, -neg_add_rev] - norm_cast - aesop - | (n : ℕ), (negSucc x, (y : ℕ)) => by - suffices - (∃ a, (n = a * y ∧ ¬n = 0) ∧ (a : ℤ) = -1 + -↑x) ↔ (n : ℤ) = (-1 + -↑x) * ↑y ∧ ¬n = 0 by - simpa [divisorsAntidiag, eq_comm, negSucc_eq] - simp only [← Int.neg_add, Int.add_comm 1, Int.neg_mul, Int.add_mul] - norm_cast - match n with - | 0 => simp - | n + 1 => simp - | .negSucc n, ((x : ℕ), (y : ℕ)) => by + grind [Nat.cast_inj] + all_goals simp [divisorsAntidiag] - norm_cast - | .negSucc n, (negSucc x, negSucc y) => by - simp [divisorsAntidiag, negSucc_eq, -neg_add_rev] - norm_cast - simp +contextual - | .negSucc n, ((x : ℕ), negSucc y) => by - simp [divisorsAntidiag, negSucc_eq, -neg_add_rev] - norm_cast - aesop - | .negSucc n, (negSucc x, (y : ℕ)) => by - simp [divisorsAntidiag, negSucc_eq, -neg_add_rev] - norm_cast - simp +contextual [eq_comm] + grind theorem image_fst_divisorsAntidiag : z.divisorsAntidiag.image Prod.fst = z.divisors := by ext From 06e0d3df10d2128318b0170e5fb2164023ccd0ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 14:42:50 +0000 Subject: [PATCH 59/65] =?UTF-8?q?feat(AlgebraicTopology/SimplicialObject):?= =?UTF-8?q?=20iterations=20of=20=CE=B4=200=20and=20=CF=83=200=20(#39079)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Mathlib.lean | 1 + .../SimplicialObject/DeltaZeroIter.lean | 148 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 Mathlib/AlgebraicTopology/SimplicialObject/DeltaZeroIter.lean diff --git a/Mathlib.lean b/Mathlib.lean index 2ef8198250..88daa4cc6e 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -1521,6 +1521,7 @@ public import Mathlib.AlgebraicTopology.SimplicialNerve public import Mathlib.AlgebraicTopology.SimplicialObject.Basic public import Mathlib.AlgebraicTopology.SimplicialObject.ChainHomotopy public import Mathlib.AlgebraicTopology.SimplicialObject.Coskeletal +public import Mathlib.AlgebraicTopology.SimplicialObject.DeltaZeroIter public import Mathlib.AlgebraicTopology.SimplicialObject.Homotopy public import Mathlib.AlgebraicTopology.SimplicialObject.II public import Mathlib.AlgebraicTopology.SimplicialObject.Op diff --git a/Mathlib/AlgebraicTopology/SimplicialObject/DeltaZeroIter.lean b/Mathlib/AlgebraicTopology/SimplicialObject/DeltaZeroIter.lean new file mode 100644 index 0000000000..e4c73113ad --- /dev/null +++ b/Mathlib/AlgebraicTopology/SimplicialObject/DeltaZeroIter.lean @@ -0,0 +1,148 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.AlgebraicTopology.SimplexCategory.DeltaZeroIter +public import Mathlib.AlgebraicTopology.SimplicialObject.Basic + +/-! +# Iterations of `δ 0` and `σ 0` + +This file introduces morphisms `δ₀Iter i` and `σ₀Iter i` for simplicial objects: +they are obtained as the `i`th iteration of `δ 0` or `σ 0`. + +-/ + +@[expose] public section + +open Simplicial + +namespace CategoryTheory.SimplicialObject + +variable {C : Type*} [Category* C] (X : SimplicialObject C) + +/-- If `X` is a simplicial object and `n + i = m`, this is the morphism +`X _⦋m⦌ ⟶ X _⦋n⦌` obtained by iterating `i` times the face map `X.δ 0`. -/ +def δ₀Iter {n m : ℕ} (i : ℕ) (hi : n + i = m := by lia) : + X _⦋m⦌ ⟶ X _⦋n⦌ := + X.map (SimplexCategory.δ₀Iter i hi).op + +@[simp] +lemma δ₀Iter_zero (n : ℕ) : X.δ₀Iter 0 (add_zero n) = 𝟙 _ := by + simp [δ₀Iter] + +@[simp] +lemma δ₀Iter_one (n : ℕ) : X.δ₀Iter 1 (n := n) rfl = X.δ 0 := rfl + +@[reassoc] +lemma δ₀Iter_succ (i : ℕ) {n m : ℕ} (h : n + i = m := by lia) : + X.δ₀Iter (i + 1) = X.δ 0 ≫ X.δ₀Iter i h := by + simp [δ₀Iter, SimplexCategory.δ₀Iter_succ _ h, δ_def] + +@[reassoc] +lemma δ₀Iter_succ' (i : ℕ) {n m : ℕ} (h : n + (i + 1) = m := by lia) : + X.δ₀Iter (i + 1) h = X.δ₀Iter i ≫ X.δ 0 := by + dsimp [δ, δ₀Iter] + rw [← Functor.map_comp, ← op_comp, SimplexCategory.δ₀Iter_succ' _ h] + +@[reassoc] +lemma δ_δ₀Iter (i : ℕ) {n m : ℕ} (j : Fin (m + 2)) + (hi : n + i = m := by lia) (hj : j.val ≤ i := by grind) : + X.δ j ≫ X.δ₀Iter i hi = X.δ₀Iter (i + 1) := by + dsimp [δ, δ₀Iter] + rw [← Functor.map_comp, ← op_comp, SimplexCategory.δ₀Iter_δ ..] + +@[reassoc] +lemma δ_δ₀Iter' {n : ℕ} (i : Fin (n + 2)) (j : ℕ) {m : ℕ} + (i' : Fin (m + 2)) (h : n + j = m := by lia) + (hi'' : i'.val = i.val + j := by grind) : + X.δ i' ≫ X.δ₀Iter j = X.δ₀Iter j ≫ X.δ i := by + dsimp [δ, δ₀Iter] + simp only [← Functor.map_comp, ← op_comp, SimplexCategory.δ₀Iter_δ' _ _ _ _ hi''] + +@[reassoc] +lemma σ_δ₀Iter (i : ℕ) {n m : ℕ} (j : Fin (m + 1)) + (hi : n + (i + 1) = m + 1 := by lia) + (hj : j.val ≤ i := by grind) : + X.σ j ≫ X.δ₀Iter (i + 1) hi = X.δ₀Iter i := by + dsimp [σ, δ₀Iter] + rw [← Functor.map_comp, ← op_comp, SimplexCategory.δ₀Iter_σ ..] + +@[reassoc] +lemma σ_δ₀Iter' (i : ℕ) {n m : ℕ} (j : Fin (m + 1)) (j' : Fin (n + 1)) + (hi' : n + i = m := by lia) + (hj' : j.val = j'.val + i := by grind) : + X.σ j ≫ X.δ₀Iter i = X.δ₀Iter i hi' ≫ X.σ j' := by + simp [σ, δ₀Iter, ← Functor.map_comp, ← op_comp, + SimplexCategory.δ₀Iter_σ' i j j'] + +/-- If `X` is a simplicial object and `n + i = m`, this is the morphism +`X _⦋n⦌ ⟶ X _⦋m⦌` obtained by iterating `i` times the degeneracy map `X.σ 0`. -/ +def σ₀Iter {n m : ℕ} (i : ℕ) (hi : n + i = m := by lia) : + X _⦋n⦌ ⟶ X _⦋m⦌ := + X.map (SimplexCategory.σ₀Iter i hi).op + +@[simp] +lemma σ₀Iter_zero (n : ℕ) : X.σ₀Iter 0 (add_zero n) = 𝟙 _ := by + simp [σ₀Iter] + +@[simp] +lemma σ₀Iter_one (n : ℕ) : X.σ₀Iter 1 (n := n) rfl = X.σ 0 := by + simp [σ₀Iter, σ_def] + +@[reassoc] +lemma σ₀Iter_succ (i : ℕ) {n m : ℕ} (h : n + (i + 1) = m := by lia) : + X.σ₀Iter (i + 1) h = X.σ 0 ≫ X.σ₀Iter i := by + dsimp [σ, σ₀Iter] + rw [← Functor.map_comp, ← op_comp, SimplexCategory.σ₀Iter_succ ..] + +@[reassoc] +lemma σ₀Iter_succ' (i : ℕ) {n m : ℕ} (h : n + i = m := by lia) : + X.σ₀Iter (i + 1) = X.σ₀Iter i h ≫ X.σ 0 := by + simp [σ₀Iter, SimplexCategory.σ₀Iter_succ' _ h, σ_def] + +@[reassoc] +lemma σ₀Iter_δ {n : ℕ} (i : Fin (n + 2)) (j : ℕ) {m : ℕ} (h : m + (j + 1) = n + 1 := by lia) + (hi' : i.val ≤ j + 1 := by grind) : + X.σ₀Iter (n := m) (j + 1) h ≫ X.δ i = X.σ₀Iter j := by + simp only [σ₀Iter, δ, ← Functor.map_comp, ← op_comp, SimplexCategory.δ_σ₀Iter i j h] + +@[reassoc] +lemma σ₀Iter_δ' {n : ℕ} (i : Fin (n + 2)) (j : ℕ) {m : ℕ} + (i' : Fin (m + 2)) (h : m + j = n := by lia) + (hi' : j < i.val := by grind) + (hi'' : i.val = i'.val + j := by grind) : + X.σ₀Iter (n := m + 1) j ≫ X.δ i = X.δ i' ≫ X.σ₀Iter j := by + simp only [σ₀Iter, δ, ← Functor.map_comp, ← op_comp, + SimplexCategory.δ_σ₀Iter' i j i'] + +@[reassoc] +lemma σ₀Iter_σ (i : ℕ) {n m : ℕ} (j : Fin (m + 1)) (hi : n + i = m := by lia) + (hj : j.val ≤ i := by grind) : + X.σ₀Iter i hi ≫ X.σ j = X.σ₀Iter (i + 1) := by + dsimp [σ, σ₀Iter] + rw [← Functor.map_comp, ← op_comp, SimplexCategory.σ_σ₀Iter ..] + +@[reassoc] +lemma σ₀Iter_σ' (i : ℕ) {n m : ℕ} (j : Fin (m + 1)) (j' : Fin (n + 1)) + (hi : n + i = m := by lia) + (hj : j.val = j'.val + i := by grind) : + X.σ₀Iter i hi ≫ X.σ j = X.σ j' ≫ X.σ₀Iter i := by + simp [σ, σ₀Iter, ← Functor.map_comp, ← op_comp, + SimplexCategory.σ_σ₀Iter' i j j'] + +@[reassoc (attr := simp)] +lemma σ₀Iter_δ₀Iter (i : ℕ) {n m : ℕ} (hi : n + i = m := by lia) : + X.σ₀Iter i hi ≫ X.δ₀Iter i hi = 𝟙 _ := by + simp [σ₀Iter, δ₀Iter, ← Functor.map_comp, ← op_comp] + +instance (i : ℕ) {n m : ℕ} (hi : n + i = m) : Mono (X.σ₀Iter i hi) := + mono_of_mono_fac (X.σ₀Iter_δ₀Iter i hi) + +instance (i : ℕ) {n m : ℕ} (hi : n + i = m) : Epi (X.δ₀Iter i hi) := + epi_of_epi_fac (X.σ₀Iter_δ₀Iter i hi) + +end CategoryTheory.SimplicialObject From 9799e69437c8590e4f2719e9848d9b04204c4875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 15:23:37 +0000 Subject: [PATCH 60/65] feat(Algebra/Homology): the homotopy fiber and the path object (#38997) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- Mathlib.lean | 1 + Mathlib/Algebra/Homology/ComplexShape.lean | 7 + Mathlib/Algebra/Homology/HomotopyCofiber.lean | 19 +- Mathlib/Algebra/Homology/HomotopyFiber.lean | 163 ++++++++++++++++++ Mathlib/Algebra/Homology/Opposite.lean | 39 +++++ 5 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 Mathlib/Algebra/Homology/HomotopyFiber.lean diff --git a/Mathlib.lean b/Mathlib.lean index 88daa4cc6e..d392fb5caf 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -643,6 +643,7 @@ public import Mathlib.Algebra.Homology.HomotopyCategory.SingleFunctors public import Mathlib.Algebra.Homology.HomotopyCategory.SpectralObject public import Mathlib.Algebra.Homology.HomotopyCategory.Triangulated public import Mathlib.Algebra.Homology.HomotopyCofiber +public import Mathlib.Algebra.Homology.HomotopyFiber public import Mathlib.Algebra.Homology.ImageToKernel public import Mathlib.Algebra.Homology.LeftResolution.Basic public import Mathlib.Algebra.Homology.LeftResolution.Reduced diff --git a/Mathlib/Algebra/Homology/ComplexShape.lean b/Mathlib/Algebra/Homology/ComplexShape.lean index 66899bc102..7db47640aa 100644 --- a/Mathlib/Algebra/Homology/ComplexShape.lean +++ b/Mathlib/Algebra/Homology/ComplexShape.lean @@ -94,6 +94,13 @@ def symm (c : ComplexShape ι) : ComplexShape ι where next_eq w w' := c.prev_eq w w' prev_eq w w' := c.next_eq w w' +/-- If `c : ComplexShape α` is such that `c.Rel` is decidable, it is also the +case of `c.symm.Rel`. -/ +@[implicit_reducible] +def decidableRelSymm {α : Type*} (c : ComplexShape α) [DecidableRel c.Rel] : + DecidableRel c.symm.Rel := + fun a b ↦ decidable_of_iff (c.Rel b a) Iff.rfl + @[simp] theorem symm_symm (c : ComplexShape ι) : c.symm.symm = c := rfl diff --git a/Mathlib/Algebra/Homology/HomotopyCofiber.lean b/Mathlib/Algebra/Homology/HomotopyCofiber.lean index 099b803017..da1b6d6faf 100644 --- a/Mathlib/Algebra/Homology/HomotopyCofiber.lean +++ b/Mathlib/Algebra/Homology/HomotopyCofiber.lean @@ -79,6 +79,16 @@ noncomputable def XIso (i : ι) (hi : ¬ c.Rel i (c.next i)) : X φ i ≅ G.X i := eqToIso (dif_neg hi) +lemma isZero_X (i : ι) (hG : IsZero (G.X i)) + (hF : ∀ (j : ι), c.Rel i j → IsZero (F.X j)) : + IsZero (X φ i) := by + by_cases h : c.Rel i (c.next i) + · haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ h + refine IsZero.of_iso ?_ (XIsoBiprod φ _ _ h) + simp only [biprod_isZero_iff] + exact ⟨hF _ h, hG⟩ + · exact hG.of_iso (XIso φ i h) + /-- The second projection `(homotopyCofiber φ).X i ⟶ G.X i`. -/ noncomputable def sndX (i : ι) : X φ i ⟶ G.X i := if hi : c.Rel i (c.next i) @@ -376,7 +386,13 @@ section variable (K) variable [∀ i, HasBinaryBiproduct (K.X i) (K.X i)] - [HasHomotopyCofiber (biprod.lift (𝟙 K) (-𝟙 K))] + +/-- Given a homological complex `K`, this is the property that the morphism +`K ⟶ K ⊞ K` induced by `𝟙 K` and `-𝟙 K` has a cofiber, which allows +to define `K.cylinder` as this cofiber. -/ +abbrev HasCylinder : Prop := HasHomotopyCofiber (biprod.lift (𝟙 K) (-𝟙 K)) + +variable [K.HasCylinder] /-- The cylinder object of a homological complex `K` is the homotopy cofiber of the morphism `biprod.lift (𝟙 K) (-𝟙 K) : K ⟶ K ⊞ K`. -/ @@ -525,6 +541,7 @@ noncomputable def πCompι₀Homotopy : Homotopy (π K ≫ ι₀ K) (𝟙 K.cyli (πCompι₀Homotopy.nullHomotopy K)) /-- The homotopy equivalence between `K.cylinder` and `K`. -/ +@[simps] noncomputable def homotopyEquiv : HomotopyEquiv K.cylinder K where hom := π K inv := ι₀ K diff --git a/Mathlib/Algebra/Homology/HomotopyFiber.lean b/Mathlib/Algebra/Homology/HomotopyFiber.lean new file mode 100644 index 0000000000..e5efe421ca --- /dev/null +++ b/Mathlib/Algebra/Homology/HomotopyFiber.lean @@ -0,0 +1,163 @@ +/- +Copyright (c) 2023 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Homology.HomotopyCofiber +public import Mathlib.Algebra.Homology.Opposite + +/-! +# The homotopy fiber of a morphism of homological complexes + +In this file, we construct the homotopy fiber of a morphism `φ : F ⟶ G` +between homological complexes. Moreover, we dualise the definition +of the cylinder (which is a particular case of a homotopy cofiber) +in order to define the path object of a homological complex. + +-/ + +@[expose] public section + +open CategoryTheory Category Limits Preadditive Opposite + +variable {C : Type*} [Category* C] [Preadditive C] + +namespace HomologicalComplex + +attribute [local instance] ComplexShape.decidableRelSymm + +variable {α : Type*} {c : ComplexShape α} {F G K : HomologicalComplex C c} (φ : F ⟶ G) + +variable [DecidableRel c.Rel] + +section + +/-- A morphism of homological complexes `φ : F ⟶ G` has a homotopy fiber if for all +indices `i` and `j` such that `c.Rel i j`, the binary biproduct `F.X i ⊞ G.X j` exists. -/ +class HasHomotopyFiber (φ : F ⟶ G) : Prop where + hasBinaryBiproduct (φ) (i j : α) (hij : c.Rel i j) : HasBinaryBiproduct (G.X i) (F.X j) + +instance [HasBinaryBiproducts C] : HasHomotopyFiber φ where + hasBinaryBiproduct _ _ _ := inferInstance + +variable [HasHomotopyFiber φ] [DecidableRel c.Rel] + +instance : HasHomotopyCofiber ((opFunctor C c).map φ.op) where + hasBinaryBiproduct i j hij := by + have := HasHomotopyFiber.hasBinaryBiproduct φ j i hij + dsimp + infer_instance + +/-- The homotopy fiber of a morphism between homological complexes. -/ +noncomputable def homotopyFiber : HomologicalComplex C c := + (unopFunctor C c.symm).obj (op (homotopyCofiber ((opFunctor C c).map φ.op))) + +end + +variable (K) [∀ i, HasBinaryBiproduct (K.X i) (K.X i)] + +instance (i : α) : HasBinaryBiproduct (K.op.X i) (K.op.X i) := by + dsimp; infer_instance + +/-- The property that a homological complex `K` has a path object, +i.e. that the morphism `K ⟶ K ⊞ K` induced by `𝟙 K` and `-𝟙 K` +has a homotopy fiber. -/ +abbrev HasPathObject := HasHomotopyFiber (biprod.desc (𝟙 K) (-𝟙 K)) + +instance [K.HasPathObject] : + HasHomotopyCofiber (biprod.lift (𝟙 K.op) (-𝟙 K.op)) where + hasBinaryBiproduct i j hij := by + have := HasHomotopyFiber.hasBinaryBiproduct (biprod.desc (𝟙 K) (-𝟙 K)) j i hij + exact hasBinaryBiproduct_of_iso (Iso.refl _ : op (K.X j) ≅ K.op.X j) + (show op ((K ⊞ K).X i) ≅ (K.op ⊞ K.op).X i from + ((eval _ _ i).mapBiprod K K).op.symm ≪≫ biprod.opIso _ _ ≪≫ + ((eval _ _ i).mapBiprod K.op K.op).symm) + +variable [K.HasPathObject] + +/-- The path object of a homological complex is defined here by dualizing +the cylinder object of `K.op`. -/ +@[no_expose] +noncomputable def pathObject := (unopFunctor C c.symm).obj (op K.op.cylinder) + +namespace pathObject + +lemma isZero_X (i : α) (h₁ : IsZero (K.X i)) (h₂ : ∀ (j : α), c.Rel j i → IsZero (K.X j)) : + IsZero (K.pathObject.X i) := by + apply IsZero.unop + dsimp [pathObject] + refine homotopyCofiber.isZero_X _ _ ?_ (fun j hj ↦ IsZero.op (h₂ _ hj)) + exact IsZero.of_iso (by simpa using h₁.op) + ((eval Cᵒᵖ c.symm i).mapBiprod K.op K.op) + +/-- The first projection `K.pathObject ⟶ K`. -/ +@[no_expose] +noncomputable def π₀ : K.pathObject ⟶ K := + (unopFunctor C c.symm).map (cylinder.ι₀ K.op).op + +/-- The second projection `K.pathObject ⟶ K`. -/ +@[no_expose] +noncomputable def π₁ : K.pathObject ⟶ K := + (unopFunctor C c.symm).map (cylinder.ι₁ K.op).op + +/-- The inclusion `K ⟶ K.pathObject`. -/ +@[no_expose] +noncomputable def ι : K ⟶ K.pathObject := + (unopFunctor C c.symm).map (cylinder.π K.op).op + +@[reassoc (attr := simp)] +lemma π₀_ι : ι K ≫ π₀ K = 𝟙 K := + Quiver.Hom.op_inj ((opFunctor C c).map_injective (cylinder.ι₀_π K.op)) + +@[reassoc (attr := simp)] +lemma π₁_ι : ι K ≫ π₁ K = 𝟙 K := + Quiver.Hom.op_inj ((opFunctor C c).map_injective (cylinder.ι₁_π K.op)) + +/-- The homotopy between `π₀ K ≫ ι K` and `𝟙 K.pathObject`. -/ +@[no_expose] +noncomputable def π₀CompιHomotopy (hc : ∀ (i : α), ∃ j, c.Rel i j) : + Homotopy (π₀ K ≫ ι K) (𝟙 K.pathObject) := + (cylinder.πCompι₀Homotopy K.op hc).unop + +/-- The homotopy equivalence between `K` and `K.pathObject`. -/ +@[simps] +noncomputable def homotopyEquiv (hc : ∀ (i : α), ∃ j, c.Rel i j) : + HomotopyEquiv K K.pathObject where + hom := ι K + inv := π₀ K + homotopyHomInvId := Homotopy.ofEq (by simp) + homotopyInvHomId := π₀CompιHomotopy K hc + +/-- The homotopy between `pathObject.ι₀ K` and `pathObject.ι₁ K`. -/ +@[no_expose] +noncomputable def homotopy₀₁ (hc : ∀ (i : α), ∃ j, c.Rel i j) : Homotopy (π₀ K) (π₁ K) := + (cylinder.homotopy₀₁ K.op hc).unop + +section + +variable {K} (φ₀ φ₁ : F ⟶ K) (h : Homotopy φ₀ φ₁) + +/-- The morphism `F ⟶ K.pathObject` that is induced by two morphisms `φ₀ φ₁ : F ⟶ K` +and a homotopy `h : Homotopy φ₀ φ₁`. -/ +@[no_expose] +noncomputable def lift : F ⟶ K.pathObject := + letI φ : K.op.cylinder ⟶ (opFunctor C c).obj (op F) := + cylinder.desc ((opFunctor C c).map φ₀.op) + ((opFunctor C c).map φ₁.op) h.op + (unopFunctor C c.symm).map φ.op + +@[reassoc (attr := simp)] +lemma lift_π₀ : lift φ₀ φ₁ h ≫ π₀ K = φ₀ := + Quiver.Hom.op_inj ((opFunctor C c).map_injective (cylinder.ι₀_desc _ _ _)) + +@[reassoc (attr := simp)] +lemma lift_π₁ : lift φ₀ φ₁ h ≫ π₁ K = φ₁ := + Quiver.Hom.op_inj ((opFunctor C c).map_injective (cylinder.ι₁_desc _ _ _)) + +end + +end pathObject + +end HomologicalComplex diff --git a/Mathlib/Algebra/Homology/Opposite.lean b/Mathlib/Algebra/Homology/Opposite.lean index 10e5d2d49f..6810dd4cd7 100644 --- a/Mathlib/Algebra/Homology/Opposite.lean +++ b/Mathlib/Algebra/Homology/Opposite.lean @@ -160,6 +160,9 @@ def opEquivalence : (HomologicalComplex V c)ᵒᵖ ≌ HomologicalComplex Vᵒ opFunctor_map_f, Hom.isoOfComponents_hom_f] exact Category.comp_id _ +instance : (opFunctor V c).IsEquivalence := (opEquivalence V c).isEquivalence_functor +instance : (opInverse V c).IsEquivalence := (opEquivalence V c).isEquivalence_inverse + set_option backward.isDefEq.respectTransparency false in /-- Auxiliary definition for `unopEquivalence`. -/ @[simps] @@ -211,6 +214,9 @@ def unopEquivalence : (HomologicalComplex Vᵒᵖ c)ᵒᵖ ≌ HomologicalComple simp only [comp_f] exact Category.comp_id _ +instance : (unopFunctor V c).IsEquivalence := (unopEquivalence V c).isEquivalence_functor +instance : (unopInverse V c).IsEquivalence := (unopEquivalence V c).isEquivalence_inverse + instance (K : HomologicalComplex V c) (i : ι) [K.HasHomology i] : K.op.HasHomology i := inferInstanceAs <| (K.sc i).op.HasHomology @@ -435,3 +441,36 @@ instance unopFunctor_additive : (@unopFunctor ι V _ c _).Additive where end end HomologicalComplex + +namespace Homotopy + +open HomologicalComplex + +variable {V : Type*} [Category* V] {ι : Type*} {c : ComplexShape ι} [Preadditive V] + +/-- The opposite of a homotopy between morphisms of homological complexes. -/ +@[simps] +def op {F G : HomologicalComplex V c} {φ₁ φ₂ : F ⟶ G} (h : Homotopy φ₁ φ₂) : + Homotopy ((opFunctor V c).map φ₁.op) ((opFunctor V c).map φ₂.op) where + hom i j := (h.hom j i).op + zero i j hij := Quiver.Hom.unop_inj (h.zero _ _ hij) + comm n := Quiver.Hom.unop_inj (by + dsimp + rw [h.comm n] + nth_rw 2 [add_comm] + rfl) + +/-- The homotopy between morphisms of homological complexes that is deduced +from a homotopy in the opposite category. -/ +@[simps] +def unop {F G : HomologicalComplex Vᵒᵖ c} {φ₁ φ₂ : F ⟶ G} (h : Homotopy φ₁ φ₂) : + Homotopy ((unopFunctor V c).map φ₁.op) ((unopFunctor V c).map φ₂.op) where + hom i j := (h.hom j i).unop + zero i j hij := Quiver.Hom.op_inj (h.zero _ _ hij) + comm n := Quiver.Hom.op_inj (by + dsimp + rw [h.comm n] + nth_rw 2 [add_comm] + rfl) + +end Homotopy From 3b9726b53c8cab07aa4bf81fab56cff8991bfdb6 Mon Sep 17 00:00:00 2001 From: Weiyi Wang Date: Sun, 10 May 2026 15:23:40 +0000 Subject: [PATCH 61/65] feat(Topology): ENat.toENNReal is a closed embedding (#39132) This is put in a new file because it needs combined import from Topology/ENat and Topology/ENNReal, and the existing Topoloty/ENNReal/Lemmas file is pretty deep in the tree so I don't want to increase the import to it. AI usage disclosure: the statement were verified by Aristotle. The proof was completely rewritten by me. --- Mathlib.lean | 1 + Mathlib/Algebra/Order/Floor/Extended.lean | 24 ++++++++++++++ .../Instances/ENNReal/ENatENNReal.lean | 32 +++++++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 Mathlib/Topology/Instances/ENNReal/ENatENNReal.lean diff --git a/Mathlib.lean b/Mathlib.lean index d392fb5caf..324d439a4a 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -7731,6 +7731,7 @@ public import Mathlib.Topology.Instances.AddCircle.Real public import Mathlib.Topology.Instances.CantorSet public import Mathlib.Topology.Instances.Complex public import Mathlib.Topology.Instances.Discrete +public import Mathlib.Topology.Instances.ENNReal.ENatENNReal public import Mathlib.Topology.Instances.ENNReal.Lemmas public import Mathlib.Topology.Instances.ENat public import Mathlib.Topology.Instances.EReal.Lemmas diff --git a/Mathlib/Algebra/Order/Floor/Extended.lean b/Mathlib/Algebra/Order/Floor/Extended.lean index 40946b6c4e..f746b8d71f 100644 --- a/Mathlib/Algebra/Order/Floor/Extended.lean +++ b/Mathlib/Algebra/Order/Floor/Extended.lean @@ -216,6 +216,30 @@ lemma ceil_add_le : ∀ (r s : ℝ≥0∞), ⌈r + s⌉ₑ ≤ ⌈r⌉ₑ + ⌈s @[simp] lemma toENNReal_iInf {ι : Sort*} (f : ι → ℕ∞) : toENNReal (⨅ i, f i) = ⨅ i, toENNReal (f i) := eq_of_forall_le_iff fun _ ↦ by simp [← ceil_le] +@[simp] lemma preimage_toENNReal_Ioi (a : ℝ≥0∞) : + toENNReal ⁻¹' Set.Ioi a = Set.Ioi ⌊a⌋ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Iio (a : ℝ≥0∞) : + toENNReal ⁻¹' Set.Iio a = Set.Iio ⌈a⌉ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Iic (a : ℝ≥0∞) : + toENNReal ⁻¹' Set.Iic a = Set.Iic ⌊a⌋ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Ici (a : ℝ≥0∞) : + toENNReal ⁻¹' Set.Ici a = Set.Ici ⌈a⌉ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Icc (a b : ℝ≥0∞) : + toENNReal ⁻¹' Set.Icc a b = Set.Icc ⌈a⌉ₑ ⌊b⌋ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Ico (a b : ℝ≥0∞) : + toENNReal ⁻¹' Set.Ico a b = Set.Ico ⌈a⌉ₑ ⌈b⌉ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Ioc (a b : ℝ≥0∞) : + toENNReal ⁻¹' Set.Ioc a b = Set.Ioc ⌊a⌋ₑ ⌊b⌋ₑ := by ext; simp + +@[simp] lemma preimage_toENNReal_Ioo (a b : ℝ≥0∞) : + toENNReal ⁻¹' Set.Ioo a b = Set.Ioo ⌊a⌋ₑ ⌈b⌉ₑ := by ext; simp + end ENat namespace Mathlib.Meta.Positivity diff --git a/Mathlib/Topology/Instances/ENNReal/ENatENNReal.lean b/Mathlib/Topology/Instances/ENNReal/ENatENNReal.lean new file mode 100644 index 0000000000..c8bfca567f --- /dev/null +++ b/Mathlib/Topology/Instances/ENNReal/ENatENNReal.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2026 Weiyi Wang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Weiyi Wang +-/ +module + +public import Mathlib.Data.Real.ENatENNReal +public import Mathlib.Topology.Instances.ENat +public import Mathlib.Topology.Instances.ENNReal.Lemmas +import Mathlib.Algebra.Order.Floor.Extended + +/-! +# Topology lemma for `ENat.toENNReal` + +This file shows `ENat.toENNReal` is a closed embedding. +-/ + +public section + +namespace ENat + +@[continuity] +theorem continuous_toENNReal : Continuous toENNReal := by + refine OrderTopology.continuous_iff.mpr fun a ↦ ⟨?_, ?_⟩ + · simpa using isOpen_Ioi + · simpa using isOpen_Iio + +theorem isClosedEmbedding_toENNReal : Topology.IsClosedEmbedding toENNReal := + continuous_toENNReal.isClosedEmbedding toENNReal_strictMono.injective + +end ENat From 7a3d8788dff5626ff387af2e05ac3fd444f5981b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Riou?= <37772949+joelriou@users.noreply.github.com> Date: Sun, 10 May 2026 15:37:47 +0000 Subject: [PATCH 62/65] feat(CategoryTheory/Localization): LocalizerMorphism.IsInduced (#39025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If `Φ : LocalizerMorphism W₁ W₂`, the typeclass `Φ.IsInduced` says that `W₂.inverseImage Φ.functor = W₁`. --- Mathlib/CategoryTheory/Equivalence.lean | 3 +- .../Localization/LocalizerMorphism.lean | 65 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/Mathlib/CategoryTheory/Equivalence.lean b/Mathlib/CategoryTheory/Equivalence.lean index 9f222687af..3fcdd28e9a 100644 --- a/Mathlib/CategoryTheory/Equivalence.lean +++ b/Mathlib/CategoryTheory/Equivalence.lean @@ -641,7 +641,8 @@ noncomputable def inv (F : C ⥤ D) [F.IsEquivalence] : D ⥤ C where set_option backward.isDefEq.respectTransparency false in /-- Interpret a functor that is an equivalence as an equivalence. -/ -@[simps functor, stacks 02C3] +@[simps functor, simps -isSimp inverse, simps! -isSimp unitIso_hom_app unitIso_inv_app + counitIso_hom_app counitIso_inv_app, stacks 02C3] noncomputable def asEquivalence (F : C ⥤ D) [F.IsEquivalence] : C ≌ D where functor := F inverse := F.inv diff --git a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean index a4188466d2..56ace99ee6 100644 --- a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean +++ b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean @@ -355,6 +355,71 @@ def arrow : LocalizerMorphism W₁.arrow W₂.arrow where functor := Φ.functor.mapArrow map _ _ _ hf := ⟨Φ.map _ hf.1, Φ.map _ hf.2⟩ +/-- If `Φ : LocalizerMorphism W₁ W₂`, the typeclass `Φ.IsInduced` +says that `W₂.inverseImage Φ.functor = W₁`. -/ +class IsInduced (Φ : LocalizerMorphism W₁ W₂) : Prop where + inverseImage_eq (Φ) : W₂.inverseImage Φ.functor = W₁ + +export IsInduced (inverseImage_eq) + +instance [Φ.IsInduced] : Φ.op.IsInduced where + inverseImage_eq := by + simp [← Φ.inverseImage_eq] + +instance : (id W₁).IsInduced where + inverseImage_eq := rfl + +instance (Ψ : LocalizerMorphism W₂ W₃) [Φ.IsInduced] [Ψ.IsInduced] : + (Φ.comp Ψ).IsInduced where + inverseImage_eq := by + simp [← Φ.inverseImage_eq, ← Ψ.inverseImage_eq] + +section + +variable [Φ.functor.IsEquivalence] [Φ.IsInduced] [W₂.RespectsIso] + +attribute [local simp] Functor.asEquivalence_counitIso_hom_app + Functor.asEquivalence_counitIso_inv_app in +/-- The inverse of a localizer morphism `Φ : LocalizerMorphism W₁ W₂`, +when `Φ.functor` is an equivalence, `W₁` is induced by `W₂` +and `W₂` respects isomorphisms. -/ +@[simps] +noncomputable def inv : LocalizerMorphism W₂ W₁ where + functor := Φ.functor.inv + map := by + simp only [← Φ.inverseImage_eq] + intro X Y f hf + exact (W₂.arrow_mk_iso_iff + (Arrow.isoMk (Φ.functor.asEquivalence.counitIso.app _) + (Φ.functor.asEquivalence.counitIso.app _))).2 hf + +instance : Φ.inv.functor.IsEquivalence := by + dsimp + infer_instance + +attribute [local simp] Functor.asEquivalence_inverse + Functor.asEquivalence_counitIso_hom_app Functor.asEquivalence_counitIso_inv_app in +instance : Φ.inv.IsInduced where + inverseImage_eq := by + ext X Y f + simp only [← Φ.inverseImage_eq] + exact W₂.arrow_mk_iso_iff + (Arrow.isoMk (Φ.functor.asEquivalence.counitIso.app _) + (Φ.functor.asEquivalence.counitIso.app _)) + +lemma isLocalizedEquivalence_of_isInduced : + Φ.IsLocalizedEquivalence := by + refine IsLocalizedEquivalence.of_equivalence _ (fun X Y f hf ↦ ?_) + let e : + Arrow.mk (Φ.functor.map (Φ.functor.preimage + ((Φ.functor.objObjPreimageIso X).hom ≫ f ≫ (Φ.functor.objObjPreimageIso Y).inv))) ≅ + Arrow.mk f := + Arrow.isoMk (Φ.functor.objObjPreimageIso X) (Φ.functor.objObjPreimageIso Y) + simp only [← Φ.inverseImage_eq] + exact ⟨_, _, _, (W₂.arrow_mk_iso_iff e).2 hf, ⟨e⟩⟩ + +end + end LocalizerMorphism end CategoryTheory From cb63a065a0ec1b0d0d327c0cf968107727d63d61 Mon Sep 17 00:00:00 2001 From: Li Xuanji Date: Sun, 10 May 2026 16:02:34 +0000 Subject: [PATCH 63/65] feat: (anti-)periodicity of complex sinh,cosh,tanh (#38392) Add antiperiodicity and periodicity theorems for complex sinh/cosh/tanh from AlexKontorovich/PrimeNumberTheoremAnd --- .../SpecialFunctions/Trigonometric/Basic.lean | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean index 405837e403..ad33732b70 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean @@ -1260,4 +1260,45 @@ theorem norm_exp_mul_exp_add_exp_neg_le_of_abs_im_le {a b : ℝ} (ha : a ≤ 0) · refine Real.cos_nonneg_of_mem_Icc ⟨?_, hb⟩ exact (neg_nonpos.2 <| Real.pi_div_two_pos.le).trans ((_root_.abs_nonneg _).trans hz) +theorem sinh_antiperiodic : Function.Antiperiodic sinh (π * I) := by + simp [Complex.sinh_add, sinh_mul_I, cosh_mul_I] + +@[simp] +theorem sinh_add_pi_mul_I (z : ℂ) : sinh (z + π * I) = -sinh z := + sinh_antiperiodic z + +theorem sinh_periodic : Function.Periodic sinh (2 * π * I) := by + convert sinh_antiperiodic.periodic_two_mul using 1 + ring + +@[simp] +theorem sinh_sub_pi_mul_I (z : ℂ) : sinh (z - π * I) = -sinh z := + sinh_antiperiodic.sub_eq z + +theorem cosh_antiperiodic : Function.Antiperiodic cosh (π * I) := by + simp [Complex.cosh_add, cosh_mul_I, sinh_mul_I] + +@[simp] +theorem cosh_add_pi_mul_I (z : ℂ) : cosh (z + π * I) = -cosh z := + cosh_antiperiodic z + +theorem cosh_periodic : Function.Periodic cosh (2 * π * I) := by + convert cosh_antiperiodic.periodic_two_mul using 1 + ring + +@[simp] +theorem cosh_sub_pi_mul_I (z : ℂ) : cosh (z - π * I) = -cosh z := + cosh_antiperiodic.sub_eq z + +theorem tanh_periodic : Function.Periodic tanh (π * I) := by + simp [tanh_eq_sinh_div_cosh] + +@[simp] +theorem tanh_add_pi_mul_I (z : ℂ) : tanh (z + π * I) = tanh z := + tanh_periodic z + +@[simp] +theorem tanh_sub_pi_mul_I (z : ℂ) : tanh (z - π * I) = tanh z := + tanh_periodic.sub_eq z + end Complex From 196aaf95cb4e39748f43734e98ea4d6d1238ac99 Mon Sep 17 00:00:00 2001 From: Chris Birkbeck <56166236+CBirkbeck@users.noreply.github.com> Date: Sun, 10 May 2026 17:50:15 +0000 Subject: [PATCH 64/65] =?UTF-8?q?feat(NumberTheory/ModularForms):=20=CE=94?= =?UTF-8?q?=20=3D=20(E=E2=82=84=C2=B3=20-=20E=E2=82=86=C2=B2)=20/=201728?= =?UTF-8?q?=20(#38806)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proves the classical identity expressing the modular discriminant as a combination of Eisenstein series: $$\Delta = \frac{E_4^3 - E_6^2}{1728}.$$ The work was done as part of the Sphere packing project. This PR was done with the help of Claude Code. (Resubmission of #37789 with a renamed branch; earlier dependency work has been merged via #37979.) --- Mathlib.lean | 5 +- Mathlib/NumberTheory/ModularForms/Basic.lean | 36 ++++++++ .../ModularForms/CuspFormSubmodule.lean | 2 +- .../ModularForms/Discriminant.lean | 29 +++++- .../EisensteinSeries/QExpansion.lean | 2 +- .../{LevelOne.lean => LevelOne/Basic.lean} | 0 .../DimensionFormula.lean} | 8 +- .../ModularForms/LevelOne/GradedRing.lean | 92 +++++++++++++++++++ .../NumberTheory/ModularForms/NormTrace.lean | 2 +- .../NumberTheory/ModularForms/QExpansion.lean | 57 +++++++++--- 10 files changed, 211 insertions(+), 22 deletions(-) rename Mathlib/NumberTheory/ModularForms/{LevelOne.lean => LevelOne/Basic.lean} (100%) rename Mathlib/NumberTheory/ModularForms/{DimensionFormulas/LevelOne.lean => LevelOne/DimensionFormula.lean} (97%) create mode 100644 Mathlib/NumberTheory/ModularForms/LevelOne/GradedRing.lean diff --git a/Mathlib.lean b/Mathlib.lean index 324d439a4a..eaa7c935f1 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -5680,7 +5680,6 @@ public import Mathlib.NumberTheory.ModularForms.Cusps public import Mathlib.NumberTheory.ModularForms.DedekindEta public import Mathlib.NumberTheory.ModularForms.Delta public import Mathlib.NumberTheory.ModularForms.Derivative -public import Mathlib.NumberTheory.ModularForms.DimensionFormulas.LevelOne public import Mathlib.NumberTheory.ModularForms.Discriminant public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Basic public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Defs @@ -5698,7 +5697,9 @@ public import Mathlib.NumberTheory.ModularForms.JacobiTheta.Bounds public import Mathlib.NumberTheory.ModularForms.JacobiTheta.Manifold public import Mathlib.NumberTheory.ModularForms.JacobiTheta.OneVariable public import Mathlib.NumberTheory.ModularForms.JacobiTheta.TwoVariable -public import Mathlib.NumberTheory.ModularForms.LevelOne +public import Mathlib.NumberTheory.ModularForms.LevelOne.Basic +public import Mathlib.NumberTheory.ModularForms.LevelOne.DimensionFormula +public import Mathlib.NumberTheory.ModularForms.LevelOne.GradedRing public import Mathlib.NumberTheory.ModularForms.NormTrace public import Mathlib.NumberTheory.ModularForms.Petersson public import Mathlib.NumberTheory.ModularForms.ProperlyDiscontinuous diff --git a/Mathlib/NumberTheory/ModularForms/Basic.lean b/Mathlib/NumberTheory/ModularForms/Basic.lean index cfb4abba9e..c1ab072b34 100644 --- a/Mathlib/NumberTheory/ModularForms/Basic.lean +++ b/Mathlib/NumberTheory/ModularForms/Basic.lean @@ -557,6 +557,7 @@ section GradedRing /-- Cast for modular forms, which is useful for avoiding `Heq`s. Optionally transports along an equality of subgroups. -/ +@[simps -fullyApplied coe] def mcast {a b : ℤ} {Γ Γ' : Subgroup (GL (Fin 2) ℝ)} (h : a = b) (f : ModularForm Γ a) (hΓ : Γ' = Γ := by rfl) : ModularForm Γ' b where toFun := (f : ℍ → ℂ) @@ -571,6 +572,18 @@ theorem gradedMonoid_eq_of_cast {Γ : Subgroup (GL (Fin 2) ℝ)} {a b : GradedMo cases h exact congr_arg _ h2 +/-- The `n`-th power of a modular form, as a modular form of weight `n * k`. -/ +def pow {Γ : Subgroup (GL (Fin 2) ℝ)} [Γ.HasDetPlusMinusOne] {k : ℤ} (f : ModularForm Γ k) + (n : ℕ) : ModularForm Γ (n * k) := + n.rec (mcast (by simp) (1 : ModularForm Γ 0)) (fun n g ↦ (g.mul f).mcast (by grind)) + +@[simp] +lemma coe_pow {Γ : Subgroup (GL (Fin 2) ℝ)} [Γ.HasDetPlusMinusOne] {k : ℤ} + (f : ModularForm Γ k) (n : ℕ) : ⇑(f.pow n) = (⇑f) ^ n := by + induction n with + | zero => simp [pow] + | succ n ih => simp_all only [pow, coe_mcast, coe_mul, pow_succ] + instance (Γ : Subgroup (GL (Fin 2) ℝ)) [Γ.HasDetPlusMinusOne] : GradedMonoid.GOne (ModularForm Γ) where one := 1 @@ -608,6 +621,29 @@ open scoped DirectSum in example (Γ : Subgroup (GL (Fin 2) ℝ)) [Γ.HasDetOne] : Algebra ℂ (⨁ i, ModularForm Γ i) := inferInstance +/-- Bridge between the auto-derived graded-monoid power `GradedMonoid.GMonoid.gnpow` and the +bespoke `ModularForm.pow`: as elements of `GradedMonoid (ModularForm Γ)`, the pair +`⟨n • k, gnpow n f⟩` agrees with `⟨n * k, f.pow n⟩`. -/ +theorem gnpow_eq_pow {Γ : Subgroup (GL (Fin 2) ℝ)} [Γ.HasDetPlusMinusOne] + {k : ℤ} (f : ModularForm Γ k) (n : ℕ) : + (⟨n • k, GradedMonoid.GMonoid.gnpow n f⟩ : GradedMonoid (ModularForm Γ)) = + ⟨(n : ℤ) * k, f.pow n⟩ := by + induction n with + | zero => + refine (GradedMonoid.GMonoid.gnpow_zero' ⟨k, f⟩).trans ?_ + exact gradedMonoid_eq_of_cast (zero_mul k).symm (ModularForm.ext fun _ ↦ rfl) + | succ n ih => + refine (GradedMonoid.GMonoid.gnpow_succ' n ⟨k, f⟩).trans ?_ + refine (congrArg (fun x : GradedMonoid (ModularForm Γ) ↦ x * ⟨k, f⟩) ih).trans ?_ + exact gradedMonoid_eq_of_cast (show ((n : ℤ) * k + k = (n + 1) * k) by ring) + (ModularForm.ext fun _ ↦ rfl) + +/-- The `n`-th power of `DirectSum.of _ k f` lands in grade `n * k` and is given by `f.pow n`. -/ +lemma directSum_of_pow {Γ : Subgroup (GL (Fin 2) ℝ)} [Γ.HasDetPlusMinusOne] + {k : ℤ} (f : ModularForm Γ k) (n : ℕ) : + (DirectSum.of (ModularForm Γ) k f) ^ n = .of (ModularForm Γ) ((n : ℤ) * k) (f.pow n) := by + grind [DirectSum.ofPow, DirectSum.of_eq_of_gradedMonoid_eq (gnpow_eq_pow f n)] + open Filter SlashInvariantForm /-- Given `ModularForm`'s `F i` of weight `k i` for `i : ι`, define the form which as a diff --git a/Mathlib/NumberTheory/ModularForms/CuspFormSubmodule.lean b/Mathlib/NumberTheory/ModularForms/CuspFormSubmodule.lean index 5d38f28dbe..dcd9a8cd69 100644 --- a/Mathlib/NumberTheory/ModularForms/CuspFormSubmodule.lean +++ b/Mathlib/NumberTheory/ModularForms/CuspFormSubmodule.lean @@ -6,7 +6,7 @@ Authors: Chris Birkbeck module public import Mathlib.NumberTheory.ModularForms.QExpansion -public import Mathlib.NumberTheory.ModularForms.LevelOne +public import Mathlib.NumberTheory.ModularForms.LevelOne.Basic public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.QExpansion /-! diff --git a/Mathlib/NumberTheory/ModularForms/Discriminant.lean b/Mathlib/NumberTheory/ModularForms/Discriminant.lean index 582d40a8fe..a489b589c9 100644 --- a/Mathlib/NumberTheory/ModularForms/Discriminant.lean +++ b/Mathlib/NumberTheory/ModularForms/Discriminant.lean @@ -10,7 +10,7 @@ public import Mathlib.Analysis.Normed.Ring.InfiniteProd public import Mathlib.NumberTheory.ModularForms.DedekindEta public import Mathlib.NumberTheory.ModularForms.Basic public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.E2.Transform -public import Mathlib.NumberTheory.ModularForms.LevelOne +public import Mathlib.NumberTheory.ModularForms.LevelOne.Basic public import Mathlib.NumberTheory.ModularForms.QExpansion /-! @@ -192,6 +192,33 @@ lemma exp_isBigO_discriminant : (fun τ ↦ Real.exp (-2 * π * τ.im)) =O[atImI grind [norm_one, norm_sub_rev] linarith [norm_nonneg (𝕢 1 τ), mul_le_mul_of_nonneg_left hprod_bound (norm_nonneg (𝕢 1 τ))] +/-- The cusp function of the discriminant equals `q * ∏' n, (1 - q^(n+1))^24` +on the open unit disc. -/ +lemma discriminant_cuspFunction_eqOn : Set.EqOn (cuspFunction 1 Δ) + (fun q ↦ q * ∏' i, (1 - q ^ (i + 1)) ^ 24) (Metric.ball 0 1) := by + intro q hq + by_cases hq0 : q = 0 + · simpa [hq0] using Periodic.cuspFunction_zero_of_zero_at_inf one_pos + discriminant_isZeroAtImInfty.zero_at_infty_comp_ofComplex + · have him := Periodic.im_invQParam_pos_of_norm_lt_one one_pos + (by simpa [dist_zero_right] using hq) hq0 + simp [cuspFunction, Periodic.cuspFunction_eq_of_nonzero 1 _ hq0, + ofComplex_apply_of_im_pos him, discriminant_eq_q_prod ⟨_, him⟩, + Periodic.qParam_right_inv one_ne_zero hq0, eta_q] + +/-- The first q-expansion coefficient of the modular discriminant is 1. -/ +lemma discriminant_qExpansion_coeff_one : (qExpansion 1 Δ).coeff 1 = 1 := by + have hmem : (0 : ℂ) ∈ Metric.ball (0 : ℂ) 1 := Metric.mem_ball_self one_pos + calc (qExpansion 1 Δ).coeff 1 + = derivWithin (cuspFunction 1 Δ) (Metric.ball 0 1) 0 := by + simp [qExpansion_coeff, ← derivWithin_of_isOpen Metric.isOpen_ball hmem] + _ = derivWithin (fun q ↦ q * ∏' i, (1 - q ^ (i + 1)) ^ 24) (Metric.ball 0 1) 0 := + derivWithin_congr discriminant_cuspFunction_eqOn (discriminant_cuspFunction_eqOn hmem) + _ = 1 := by + simp [derivWithin_fun_mul differentiableWithinAt_id' + (differentiableOn_tprod_one_sub_pow_pow 24 _ hmem), + derivWithin_id' _ _ (Metric.isOpen_ball.uniqueDiffWithinAt hmem)] + end end ModularForm diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean index dd08d30f3e..b6d65d4acf 100644 --- a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean @@ -10,7 +10,7 @@ public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Cotangent public import Mathlib.NumberTheory.LSeries.Dirichlet public import Mathlib.NumberTheory.LSeries.HurwitzZetaValues public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.Basic -public import Mathlib.NumberTheory.ModularForms.LevelOne +public import Mathlib.NumberTheory.ModularForms.LevelOne.Basic public import Mathlib.NumberTheory.TsumDivisorsAntidiagonal import Mathlib.Topology.EMetricSpace.Paracompact diff --git a/Mathlib/NumberTheory/ModularForms/LevelOne.lean b/Mathlib/NumberTheory/ModularForms/LevelOne/Basic.lean similarity index 100% rename from Mathlib/NumberTheory/ModularForms/LevelOne.lean rename to Mathlib/NumberTheory/ModularForms/LevelOne/Basic.lean diff --git a/Mathlib/NumberTheory/ModularForms/DimensionFormulas/LevelOne.lean b/Mathlib/NumberTheory/ModularForms/LevelOne/DimensionFormula.lean similarity index 97% rename from Mathlib/NumberTheory/ModularForms/DimensionFormulas/LevelOne.lean rename to Mathlib/NumberTheory/ModularForms/LevelOne/DimensionFormula.lean index 0ddf7a000f..dd37aa7f69 100644 --- a/Mathlib/NumberTheory/ModularForms/DimensionFormulas/LevelOne.lean +++ b/Mathlib/NumberTheory/ModularForms/LevelOne/DimensionFormula.lean @@ -136,8 +136,8 @@ lemma ModularForm.rank_eq_one_add_rank_cuspForm {k : ℕ} (hk : 3 ≤ k) (hk2 : exact one_ne_zero <| hE.symm.trans <| (isCuspForm_iff_coeffZero_eq_zero _).mp h · refine (Submodule.Quotient.forall _).mpr fun f ↦ ⟨(qExpansion 1 f).coeff 0, ?_⟩ rw [← Submodule.Quotient.mk_smul, Submodule.Quotient.eq, mem_cuspFormSubmodule_iff, - isCuspForm_iff_coeffZero_eq_zero, ModularForm.coe_sub, ModularFormClass.qExpansion_sub, - IsGLPos.coe_smul, ModularFormClass.qExpansion_smul, map_sub, + isCuspForm_iff_coeffZero_eq_zero, ModularForm.coe_sub, ModularForm.qExpansion_sub, + IsGLPos.coe_smul, ModularForm.qExpansion_smul, map_sub, PowerSeries.coeff_smul, E_qExpansion_coeff_zero hk hk2, smul_eq_mul, mul_one, sub_self] all_goals simp @@ -187,12 +187,12 @@ private lemma weight_two_qExpansion_eq_zero (f : ModularForm 𝒮ℒ 2) : qExpan (Module.rank_eq_one_iff_finrank_eq_one.mp levelOne_weight_six_rank_one) _ have hqc4 : c4 • qExpansion 1 (E₄ : ℍ → ℂ) = qExpansion 1 (f : ℍ → ℂ) ^ 2 := by rw [pow_two, ← ModularForm.qExpansion_mul one_pos one_mem_strictPeriods_SL f f, - ← ModularFormClass.qExpansion_smul one_pos one_mem_strictPeriods_SL c4 E₄, + ← ModularForm.qExpansion_smul one_pos one_mem_strictPeriods_SL c4 E₄, show (c4 • E₄ : ℍ → ℂ) = (f.mul f) from congrArg DFunLike.coe hc4] have hqc6 : c6 • qExpansion 1 E₆ = qExpansion 1 (f : ℍ → ℂ) ^ 3 := by rw [pow_succ, pow_two, ← ModularForm.qExpansion_mul one_pos one_mem_strictPeriods_SL f f, ← ModularForm.qExpansion_mul one_pos one_mem_strictPeriods_SL (f.mul f) f, - ← ModularFormClass.qExpansion_smul one_pos one_mem_strictPeriods_SL c6 E₆, + ← ModularForm.qExpansion_smul one_pos one_mem_strictPeriods_SL c6 E₆, show (c6 • E₆ : ℍ → ℂ) = (f.mul f).mul f from congrArg DFunLike.coe hc6] exact eq_zero_of_pow_eq_smul (E_qExpansion_coeff_zero _ ⟨2, rfl⟩) (E_qExpansion_coeff_zero _ ⟨3, rfl⟩) E₄_qExpansion_coeff_one E₆_qExpansion_coeff_one hqc4 hqc6 diff --git a/Mathlib/NumberTheory/ModularForms/LevelOne/GradedRing.lean b/Mathlib/NumberTheory/ModularForms/LevelOne/GradedRing.lean new file mode 100644 index 0000000000..6fba210898 --- /dev/null +++ b/Mathlib/NumberTheory/ModularForms/LevelOne/GradedRing.lean @@ -0,0 +1,92 @@ +/- +Copyright (c) 2026 Chris Birkbeck. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Chris Birkbeck +-/ +module + +public import Mathlib.NumberTheory.ModularForms.LevelOne.DimensionFormula + +/-! +# The graded ring of level-1 modular forms + +This file collects structural results about the graded ring `⨁ k, ModularForm 𝒮ℒ k` of +level-1 modular forms, beyond those that fall out of the dimension formula directly. + +## Main results + +* `ModularForm.discriminant_eq_E₄_cube_sub_E₆_sq`: the pointwise identity + `Δ = (E₄³ - E₆²) / 1728`. +* `ModularForm.discriminant_eq_E₄_cube_sub_E₆_sq_graded`: the same identity in the graded + ring `⨁ k, ModularForm 𝒮ℒ k`. +-/ + +public noncomputable section + +open UpperHalfPlane ModularForm ModularFormClass MatrixGroups EisensteinSeries + +namespace ModularForm + +/-- The combination `E₄³ - E₆²` viewed as a level-1 modular form of weight 12. -/ +private noncomputable def E₄CubeSubE₆SqForm : ModularForm 𝒮ℒ 12 := + ModularForm.mcast (by decide) (E₄.pow 3) - ModularForm.mcast (by decide) (E₆.pow 2) + +private lemma E₄CubeSubE₆SqForm_apply (z : ℍ) : + E₄CubeSubE₆SqForm z = E₄ z ^ 3 - E₆ z ^ 2 := by + simp only [E₄CubeSubE₆SqForm, coe_mcast, coe_pow, sub_apply, Pi.pow_apply] + +private lemma E₄CubeSubE₆SqForm_qExpansion_eq : + qExpansion 1 E₄CubeSubE₆SqForm = qExpansion 1 E₄ * qExpansion 1 E₄ * qExpansion 1 E₄ - + qExpansion 1 E₆ * qExpansion 1 E₆ := by + simp only [E₄CubeSubE₆SqForm, coe_sub, coe_mcast, + ModularForm.qExpansion_sub one_pos one_mem_strictPeriods_SL, + ModularForm.qExpansion_pow one_pos one_mem_strictPeriods_SL] + ring + +private lemma E₄CubeSubE₆SqForm_isCuspForm : IsCuspForm E₄CubeSubE₆SqForm := by + simp [isCuspForm_iff_coeffZero_eq_zero, E₄CubeSubE₆SqForm_qExpansion_eq, + PowerSeries.coeff_mul, -PowerSeries.coeff_zero_eq_constantCoeff, + E_qExpansion_coeff_zero _ ⟨2, rfl⟩, E_qExpansion_coeff_zero _ ⟨3, rfl⟩] + +private lemma E₄CubeSubE₆SqForm_qExpansion_coeff_one : + (qExpansion 1 E₄CubeSubE₆SqForm).coeff 1 = 1728 := by + rw [E₄CubeSubE₆SqForm_qExpansion_eq] + norm_num [PowerSeries.coeff_mul, Finset.Nat.antidiagonal_succ, E₄_qExpansion_coeff_one, + E₆_qExpansion_coeff_one, E_qExpansion_coeff_zero _ ⟨2, rfl⟩, + E_qExpansion_coeff_zero _ ⟨3, rfl⟩] + +/-- The modular discriminant equals `(E₄³ - E₆²) / 1728`. -/ +theorem discriminant_eq_E₄_cube_sub_E₆_sq (z : ℍ) : + discriminant z = (E₄ z ^ 3 - E₆ z ^ 2) / 1728 := by + obtain ⟨g, hg⟩ := E₄CubeSubE₆SqForm_isCuspForm + obtain ⟨c, hc⟩ := CuspForm.exists_smul_discriminant_of_weight_eq_twelve g + have hgE : (g : ℍ → ℂ) = E₄CubeSubE₆SqForm := congrArg DFunLike.coe hg + have hc_eq : c = 1728 := by + have hcΔ : (c • CuspForm.discriminant : ℍ → ℂ) = g := congrArg DFunLike.coe hc + have hgΔ := ModularForm.qExpansion_smul one_pos one_mem_strictPeriods_SL c + CuspForm.discriminant + rw [hcΔ, hgE] at hgΔ + simpa [PowerSeries.coeff_smul, discriminant_qExpansion_coeff_one, + E₄CubeSubE₆SqForm_qExpansion_coeff_one] using (congr_arg (·.coeff 1) hgΔ).symm + have h1728 : (1728 : ℂ) * discriminant z = E₄ z ^ 3 - E₆ z ^ 2 := by + rw [← hc_eq, show c * discriminant z = (c • CuspForm.discriminant) z from rfl, hc, + congr_fun hgE z, E₄CubeSubE₆SqForm_apply] + linear_combination h1728 / 1728 + +/-- The modular discriminant equals `(E₄³ - E₆²) / 1728` in the graded ring +`⨁ k, ModularForm 𝒮ℒ k`. -/ +theorem discriminant_eq_E₄_cube_sub_E₆_sq_graded : + DirectSum.of (ModularForm 𝒮ℒ) 12 CuspForm.discriminant = + (1 / 1728 : ℂ) • (.of (ModularForm 𝒮ℒ) 4 E₄ ^ 3 - .of (ModularForm 𝒮ℒ) 6 E₆ ^ 2) := by + simp only [ModularForm.directSum_of_pow] + change DirectSum.of (ModularForm 𝒮ℒ) 12 CuspForm.discriminant = (1 / 1728 : ℂ) • + (DirectSum.of (ModularForm 𝒮ℒ) 12 (E₄.pow 3) - DirectSum.of (ModularForm 𝒮ℒ) 12 (E₆.pow 2)) + rw [← map_sub (DirectSum.of (ModularForm 𝒮ℒ) 12), ← DirectSum.of_smul] + congr 1 + ext z + change ModularForm.discriminant z = (1 / 1728 : ℂ) * (E₄ z ^ 3 - E₆ z ^ 2) + grind [discriminant_eq_E₄_cube_sub_E₆_sq z] + +end ModularForm + +end diff --git a/Mathlib/NumberTheory/ModularForms/NormTrace.lean b/Mathlib/NumberTheory/ModularForms/NormTrace.lean index 33837fc1a7..b1e0731fd3 100644 --- a/Mathlib/NumberTheory/ModularForms/NormTrace.lean +++ b/Mathlib/NumberTheory/ModularForms/NormTrace.lean @@ -5,7 +5,7 @@ Authors: David Loeffler -/ module -public import Mathlib.NumberTheory.ModularForms.LevelOne +public import Mathlib.NumberTheory.ModularForms.LevelOne.Basic /-! # Norm and trace maps diff --git a/Mathlib/NumberTheory/ModularForms/QExpansion.lean b/Mathlib/NumberTheory/ModularForms/QExpansion.lean index 87d52a7155..cd17cb79e0 100644 --- a/Mathlib/NumberTheory/ModularForms/QExpansion.lean +++ b/Mathlib/NumberTheory/ModularForms/QExpansion.lean @@ -520,7 +520,7 @@ lemma qExpansion_one (h) : qExpansion h (1 : ℍ → ℂ) = 1 := by end UpperHalfPlane -namespace ModularFormClass +namespace ModularForm protected lemma cuspFunction_smul (hh : 0 < h) (hΓ : h ∈ Γ.strictPeriods) (a : ℂ) (f : F) [ModularFormClass F Γ k] : cuspFunction h (a • f) = a • cuspFunction h f := @@ -542,6 +542,12 @@ protected lemma cuspFunction_sub {G : Type*} [FunLike G ℍ ℂ] (hh : 0 < h) cuspFunction_sub (ModularFormClass.analyticAt_cuspFunction_zero f hh hΓ).continuousAt (ModularFormClass.analyticAt_cuspFunction_zero g hh hΓ).continuousAt +protected lemma cuspFunction_mul [Γ.HasDetPlusMinusOne] (hh : 0 < h) + (hΓ : h ∈ Γ.strictPeriods) {a b : ℤ} (f : ModularForm Γ a) (g : ModularForm Γ b) : + cuspFunction h (f.mul g) = cuspFunction h f * cuspFunction h g := + cuspFunction_mul (ModularFormClass.analyticAt_cuspFunction_zero f hh hΓ).continuousAt + (ModularFormClass.analyticAt_cuspFunction_zero g hh hΓ).continuousAt + protected lemma qExpansion_smul (hh : 0 < h) (hΓ : h ∈ Γ.strictPeriods) (a : ℂ) (f : F) [ModularFormClass F Γ k] : qExpansion h (a • f) = a • qExpansion h f := qExpansion_smul (ModularFormClass.analyticAt_cuspFunction_zero f hh hΓ) a @@ -562,16 +568,6 @@ protected lemma qExpansion_sub {G : Type*} [FunLike G ℍ ℂ] (hh : 0 < h) qExpansion_sub (ModularFormClass.analyticAt_cuspFunction_zero f hh hΓ) (ModularFormClass.analyticAt_cuspFunction_zero g hh hΓ) -end ModularFormClass - -namespace ModularForm - -protected lemma cuspFunction_mul [Γ.HasDetPlusMinusOne] (hh : 0 < h) - (hΓ : h ∈ Γ.strictPeriods) {a b : ℤ} (f : ModularForm Γ a) (g : ModularForm Γ b) : - cuspFunction h (f.mul g) = cuspFunction h f * cuspFunction h g := - cuspFunction_mul (ModularFormClass.analyticAt_cuspFunction_zero f hh hΓ).continuousAt - (ModularFormClass.analyticAt_cuspFunction_zero g hh hΓ).continuousAt - protected lemma qExpansion_mul [Γ.HasDetPlusMinusOne] (hh : 0 < h) (hΓ : h ∈ Γ.strictPeriods) {a b : ℤ} (f : ModularForm Γ a) (g : ModularForm Γ b) : qExpansion h (f.mul g) = qExpansion h f * qExpansion h g := @@ -587,12 +583,21 @@ protected lemma qExpansion_one [Γ.HasDetPlusMinusOne] : qExpansion h (1 : ModularForm Γ 0) = 1 := by simp [qExpansion_one] +protected lemma qExpansion_pow [Γ.HasDetPlusMinusOne] (hh : 0 < h) + (hΓ : h ∈ Γ.strictPeriods) (f : ModularForm Γ k) (n : ℕ) : + qExpansion h (f.pow n) = (qExpansion h f) ^ n := by + induction n with + | zero => simp only [coe_pow, pow_zero, qExpansion_one] + | succ n ih => + rw [coe_pow, pow_succ, ← coe_pow, ← coe_mul, ModularForm.qExpansion_mul hh hΓ, ih, + pow_succ] + /-- The qExpansion map as an additive group hom. to power series over `ℂ`. -/ def qExpansionAddHom (hh : 0 < h) (hΓ : h ∈ Γ.strictPeriods) (k : ℤ) : ModularForm Γ k →+ PowerSeries ℂ where toFun f := qExpansion h f map_zero' := qExpansion_zero h - map_add' f g := ModularFormClass.qExpansion_add hh hΓ f g + map_add' f g := ModularForm.qExpansion_add hh hΓ f g open scoped DirectSum in /-- The qExpansion map as a map from the graded ring of modular forms to power series over `ℂ`. -/ @@ -621,6 +626,34 @@ lemma qExpansion_of_pow [Γ.HasDetPlusMinusOne] (hh : 0 < h) end ModularForm +namespace ModularFormClass + +@[deprecated (since := "2026-05-05")] +protected alias cuspFunction_smul := ModularForm.cuspFunction_smul + +@[deprecated (since := "2026-05-05")] +protected alias cuspFunction_neg := ModularForm.cuspFunction_neg + +@[deprecated (since := "2026-05-05")] +protected alias cuspFunction_add := ModularForm.cuspFunction_add + +@[deprecated (since := "2026-05-05")] +protected alias cuspFunction_sub := ModularForm.cuspFunction_sub + +@[deprecated (since := "2026-05-05")] +protected alias qExpansion_smul := ModularForm.qExpansion_smul + +@[deprecated (since := "2026-05-05")] +protected alias qExpansion_neg := ModularForm.qExpansion_neg + +@[deprecated (since := "2026-05-05")] +protected alias qExpansion_add := ModularForm.qExpansion_add + +@[deprecated (since := "2026-05-05")] +protected alias qExpansion_sub := ModularForm.qExpansion_sub + +end ModularFormClass + end ring section uniqueness From 706bea155cb08b692c202d4080da25c88fb8030a Mon Sep 17 00:00:00 2001 From: FordUniver <61389961+FordUniver@users.noreply.github.com> Date: Sun, 10 May 2026 18:04:06 +0000 Subject: [PATCH 65/65] feat(Analysis/SpecialFunctions/Pow/Continuity): add Filter.Tendsto.rpow_const_nhds_zero (#39030) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Specialises `Filter.Tendsto.rpow_const` to base `→ 0`. The strict `0 < p` (rather than `0 ≤ p` from the parent) is needed to identify `0 ^ p` with `0`. Refactors the one in-tree call site (`ZetaAsymp.term_tsum_of_lt`). Co-authored-by: Sebastian Pokutta <23001135+pokutta@users.noreply.github.com> Co-authored-by: Christoph Spiegel --- Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean | 5 +++++ Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean b/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean index ee111a114b..0175e7acf4 100644 --- a/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean +++ b/Mathlib/Analysis/SpecialFunctions/Pow/Continuity.lean @@ -243,6 +243,11 @@ theorem Filter.Tendsto.rpow_const {l : Filter α} {f : α → ℝ} {x p : ℝ} ( if h0 : 0 = p then h0 ▸ by simp [tendsto_const_nhds] else hf.rpow tendsto_const_nhds (h.imp id fun h' => h'.lt_of_ne h0) +theorem Filter.Tendsto.rpow_const_nhds_zero {l : Filter α} {f : α → ℝ} {p : ℝ} + (hf : Tendsto f l (𝓝 0)) (hp : 0 < p) : + Tendsto (fun t ↦ f t ^ p) l (𝓝 0) := + Real.zero_rpow hp.ne' ▸ hf.rpow_const (.inr hp.le) + variable [TopologicalSpace α] {f g : α → ℝ} {s : Set α} {x : α} {p : ℝ} nonrec theorem ContinuousAt.rpow (hf : ContinuousAt f x) (hg : ContinuousAt g x) diff --git a/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean b/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean index 45989e41e3..92452a06dc 100644 --- a/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean +++ b/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean @@ -215,9 +215,8 @@ lemma term_tsum_of_lt {s : ℝ} (hs : 1 < s) : · exact_mod_cast (summable_nat_add_iff 1).mpr (summable_one_div_nat_rpow.mpr hs) · apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds · change Tendsto (fun n : ℕ ↦ (1 / ↑(n + 1) : ℝ) ^ (s - 1)) .. - rw [show 𝓝 (0 : ℝ) = 𝓝 (0 ^ (s - 1)) by rw [zero_rpow]; linarith] - refine Tendsto.rpow_const ?_ (Or.inr <| by linarith) - exact (tendsto_const_div_atTop_nhds_zero_nat _).comp (tendsto_add_atTop_nat _) + exact ((tendsto_const_div_atTop_nhds_zero_nat _).comp + (tendsto_add_atTop_nat _)).rpow_const_nhds_zero (by linarith) · intro n positivity · intro n